001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019
020 package org.apache.geronimo.plugin.car;
021
022 import java.io.File;
023 import java.io.StringWriter;
024
025 import java.util.Iterator;
026 import java.util.LinkedHashSet;
027 import java.util.List;
028 import java.util.Properties;
029
030 import javax.xml.namespace.QName;
031
032 import org.apache.geronimo.deployment.service.EnvironmentBuilder;
033 import org.apache.geronimo.deployment.xbeans.ArtifactType;
034 import org.apache.geronimo.deployment.xbeans.EnvironmentType;
035 import org.apache.geronimo.kernel.repository.Environment;
036 import org.apache.geronimo.kernel.repository.Artifact;
037 import org.apache.geronimo.kernel.repository.ImportType;
038
039 import org.apache.maven.model.Dependency;
040
041 import org.apache.velocity.Template;
042 import org.apache.velocity.VelocityContext;
043 import org.apache.velocity.app.VelocityEngine;
044
045 import org.apache.xmlbeans.XmlCursor;
046 import org.apache.xmlbeans.XmlObject;
047 import org.apache.xmlbeans.XmlOptions;
048 import org.apache.xmlbeans.XmlCursor.TokenType;
049
050 //
051 // TODO: Rename to PreparePlanMojo
052 //
053
054 /**
055 * Add dependencies to a plan and process with velocity
056 *
057 * @goal prepare-plan
058 *
059 * @version $Rev: 451661 $ $Date: 2006-09-30 13:45:53 -0700 (Sat, 30 Sep 2006) $
060 */
061 public class PlanProcessorMojo
062 extends AbstractCarMojo
063 {
064 private static final String ENVIRONMENT_LOCAL_NAME = "environment";
065
066 private static final QName ENVIRONMENT_QNAME = new QName("http://geronimo.apache.org/xml/ns/deployment-1.2", "environment");
067
068 /**
069 * @parameter expression="${basedir}/src/plan"
070 * @required
071 */
072 protected File sourceDir = null;
073
074 /**
075 * @parameter expression="${project.build.directory}/plan"
076 * @required
077 */
078 protected File targetDir = null;
079
080 /**
081 * @parameter default-value="plan.xml"
082 * @required
083 */
084 protected String planFileName = null;
085
086 /**
087 * @parameter expression="${project.build.directory}/plan/plan.xml"
088 * @required
089 */
090 protected File targetFile = null;
091
092 private VelocityContext createContext() {
093 VelocityContext context = new VelocityContext();
094
095 // Load properties, It inherits them all!
096 Properties props = project.getProperties();
097 for (Iterator iter = props.keySet().iterator(); iter.hasNext();) {
098 String key = (String) iter.next();
099 String value = props.getProperty(key);
100
101 log.debug("Setting " + key + "=" + value);
102 context.put(key, value);
103 }
104
105 context.put("pom", project);
106
107 return context;
108 }
109
110 protected void doExecute() throws Exception {
111 //
112 // FIXME: Do not need velocity here, we only need to filter,
113 // could use resources plugin to do this for us, or
114 // implement what resources plugin does here
115 //
116
117 VelocityContext context = createContext();
118
119 VelocityEngine velocity = new VelocityEngine();
120 velocity.setProperty(VelocityEngine.FILE_RESOURCE_LOADER_PATH, sourceDir.getAbsolutePath());
121 velocity.init();
122
123 Template template = velocity.getTemplate(planFileName);
124 StringWriter writer = new StringWriter();
125 template.merge(context, writer);
126
127 String plan = writer.toString();
128
129 XmlObject doc = XmlObject.Factory.parse(plan);
130 XmlCursor xmlCursor = doc.newCursor();
131 LinkedHashSet dependencies = toDependencies();
132 Artifact configId = new Artifact(project.getGroupId(), project.getArtifactId(), project.getVersion(), "car");
133
134 try {
135 mergeEnvironment(xmlCursor, configId, dependencies);
136
137 if (targetDir.exists()) {
138 if (!targetDir.isDirectory()) {
139 throw new RuntimeException("TargetDir: " + this.targetDir + " exists and is not a directory");
140 }
141 }
142 else {
143 targetDir.mkdirs();
144 }
145
146 XmlOptions xmlOptions = new XmlOptions();
147 xmlOptions.setSavePrettyPrint();
148 doc.save(targetFile, xmlOptions);
149
150 log.info("Generated: " + targetFile);
151 }
152 finally {
153 xmlCursor.dispose();
154 }
155 }
156
157 void mergeEnvironment(final XmlCursor xmlCursor, final Artifact configId, final LinkedHashSet dependencies) {
158 moveToFirstStartElement(xmlCursor);
159
160 boolean atLeastOneChild = xmlCursor.toFirstChild();
161 if (!atLeastOneChild) {
162 // this is an empty element. Move to EndToken such XmlCursor.beginElement inserts an element inside it.
163 xmlCursor.toEndToken();
164 }
165 QName childName = xmlCursor.getName();
166 Environment oldEnvironment;
167
168 if (childName != null && childName.getLocalPart().equals(ENVIRONMENT_LOCAL_NAME)) {
169 convertElement(xmlCursor, ENVIRONMENT_QNAME.getNamespaceURI());
170 XmlObject xmlObject = xmlCursor.getObject();
171 EnvironmentType environmentType = (EnvironmentType) xmlObject.copy().changeType(EnvironmentType.type);
172 oldEnvironment = EnvironmentBuilder.buildEnvironment(environmentType);
173 xmlCursor.removeXml();
174 } else {
175 oldEnvironment = new Environment();
176 }
177
178 Environment newEnvironment = new Environment();
179 newEnvironment.setConfigId(configId);
180 newEnvironment.setDependencies(dependencies);
181
182 EnvironmentBuilder.mergeEnvironments(oldEnvironment, newEnvironment);
183 EnvironmentType environmentType = EnvironmentBuilder.buildEnvironmentType(oldEnvironment);
184
185 xmlCursor.beginElement(ENVIRONMENT_QNAME);
186 XmlCursor element = environmentType.newCursor();
187
188 try {
189 element.copyXmlContents(xmlCursor);
190 }
191 finally {
192 element.dispose();
193 }
194 }
195
196 private void moveToFirstStartElement(XmlCursor xmlCursor) throws AssertionError {
197 xmlCursor.toStartDoc();
198 xmlCursor.toFirstChild();
199 while (!xmlCursor.currentTokenType().isStart()) {
200 if (!xmlCursor.toNextSibling()) {
201 break;
202 }
203 }
204 if (!xmlCursor.currentTokenType().isStart()) {
205 throw new AssertionError("Cannot find first start element");
206 }
207 }
208
209 private void convertElement(final XmlCursor cursor, final String namespace) {
210 cursor.push();
211 XmlCursor end = cursor.newCursor();
212
213 try {
214 end.toCursor(cursor);
215 end.toEndToken();
216
217 while (cursor.hasNextToken() && cursor.isLeftOf(end)) {
218 if (cursor.isStart()) {
219 if (!namespace.equals(cursor.getName().getNamespaceURI())) {
220 cursor.setName(new QName(namespace, cursor.getName().getLocalPart()));
221 }
222 }
223
224 cursor.toNextToken();
225 }
226
227 cursor.pop();
228 }
229 finally {
230 end.dispose();
231 }
232 }
233
234 private LinkedHashSet toDependencies() {
235 List artifacts = project.getDependencies();
236 LinkedHashSet dependencies = new LinkedHashSet();
237
238 Iterator iter = artifacts.iterator();
239 while (iter.hasNext()) {
240 Dependency dependency = (Dependency) iter.next();
241
242 //
243 // HACK: Does not appear that we can get the "extention" status of a dependency,
244 // so specifically exclude the ones that we know about, like genesis
245 //
246
247 if (dependency.getGroupId().startsWith("org.apache.geronimo.genesis")) {
248 continue;
249 }
250
251 org.apache.geronimo.kernel.repository.Dependency gdep = toGeronimoDependency(dependency);
252 if (gdep != null) {
253 dependencies.add(gdep);
254 }
255 }
256
257 return dependencies;
258 }
259
260 private static org.apache.geronimo.kernel.repository.Dependency toGeronimoDependency(final Dependency dependency) {
261 Artifact artifact = toGeronimoArtifact(dependency);
262 String type = dependency.getType();
263 String scope = dependency.getScope();
264 String groupId = dependency.getGroupId();
265
266 //!"org.apache.geronimo.specs".equals(groupId) jacc spec needed in plan.xml
267 if ("jar".equalsIgnoreCase(type) && !"junit".equals(groupId) && (scope == null || !scope.equals("provided"))) {
268 if (dependency.getVersion() != null) {
269 artifact = new Artifact(
270 artifact.getGroupId(),
271 artifact.getArtifactId(),
272 dependency.getVersion(),
273 artifact.getType());
274 }
275 return new org.apache.geronimo.kernel.repository.Dependency(artifact, ImportType.CLASSES);
276 }
277 else if ("car".equalsIgnoreCase(type) && ("runtime").equalsIgnoreCase(scope)) {
278 return new org.apache.geronimo.kernel.repository.Dependency(artifact, ImportType.SERVICES);
279 }
280 else if ("car".equalsIgnoreCase(type) && ("compile".equalsIgnoreCase(scope))) {
281 return new org.apache.geronimo.kernel.repository.Dependency(artifact, ImportType.CLASSES);
282 }
283 else if ("car".equalsIgnoreCase(type) && (scope == null)) { //parent
284 return new org.apache.geronimo.kernel.repository.Dependency(artifact, ImportType.ALL);
285 }
286 else {
287 // not one of ours
288 return null;
289 }
290 }
291
292 private static Artifact toGeronimoArtifact(final Dependency dependency) {
293 String groupId = dependency.getGroupId();
294 String artifactId = dependency.getArtifactId();
295 String version = null;
296 String type = dependency.getType();
297
298 return new Artifact(groupId, artifactId, version, type);
299 }
300
301 interface Inserter {
302 ArtifactType insert(EnvironmentType environmentType);
303 }
304 }