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.mavenplugins.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.Velocity;
044 import org.apache.velocity.app.VelocityEngine;
045
046 import org.apache.xmlbeans.XmlCursor;
047 import org.apache.xmlbeans.XmlObject;
048 import org.apache.xmlbeans.XmlOptions;
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: 507395 $ $Date: 2007-02-14 00:36:23 -0500 (Wed, 14 Feb 2007) $
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 // Also velocity does not handle property expansion of expressions like
117 // ${foo.bar} to the value of the "foo.bar" property :-(
118 //
119 // Might be better of just hand rolling something...
120 //
121
122 VelocityContext context = createContext();
123
124 VelocityEngine velocity = new VelocityEngine();
125 velocity.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, sourceDir.getAbsolutePath());
126
127 // Don't spit out any logs
128 velocity.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM_CLASS, "org.apache.velocity.runtime.log.NullLogSystem");
129 velocity.init();
130
131 Template template = velocity.getTemplate(planFileName);
132 StringWriter writer = new StringWriter();
133 template.merge(context, writer);
134
135 String plan = writer.toString();
136
137 XmlObject doc = XmlObject.Factory.parse(plan);
138 XmlCursor xmlCursor = doc.newCursor();
139 LinkedHashSet dependencies = toDependencies();
140 Artifact configId = new Artifact(project.getGroupId(), project.getArtifactId(), project.getVersion(), "car");
141
142 try {
143 mergeEnvironment(xmlCursor, configId, dependencies);
144
145 if (targetDir.exists()) {
146 if (!targetDir.isDirectory()) {
147 throw new RuntimeException("TargetDir: " + this.targetDir + " exists and is not a directory");
148 }
149 }
150 else {
151 targetDir.mkdirs();
152 }
153
154 XmlOptions xmlOptions = new XmlOptions();
155 xmlOptions.setSavePrettyPrint();
156 doc.save(targetFile, xmlOptions);
157
158 log.info("Generated: " + targetFile);
159 }
160 finally {
161 xmlCursor.dispose();
162 }
163 }
164
165 void mergeEnvironment(final XmlCursor xmlCursor, final Artifact configId, final LinkedHashSet dependencies) {
166 moveToFirstStartElement(xmlCursor);
167
168 boolean atLeastOneChild = xmlCursor.toFirstChild();
169 if (!atLeastOneChild) {
170 // this is an empty element. Move to EndToken such XmlCursor.beginElement inserts an element inside it.
171 xmlCursor.toEndToken();
172 }
173 QName childName = xmlCursor.getName();
174 Environment oldEnvironment;
175
176 if (childName != null && childName.getLocalPart().equals(ENVIRONMENT_LOCAL_NAME)) {
177 convertElement(xmlCursor, ENVIRONMENT_QNAME.getNamespaceURI());
178 XmlObject xmlObject = xmlCursor.getObject();
179 EnvironmentType environmentType = (EnvironmentType) xmlObject.copy().changeType(EnvironmentType.type);
180 oldEnvironment = EnvironmentBuilder.buildEnvironment(environmentType);
181 xmlCursor.removeXml();
182 } else {
183 oldEnvironment = new Environment();
184 }
185
186 Environment newEnvironment = new Environment();
187 newEnvironment.setConfigId(configId);
188 newEnvironment.setDependencies(dependencies);
189
190 EnvironmentBuilder.mergeEnvironments(oldEnvironment, newEnvironment);
191 EnvironmentType environmentType = EnvironmentBuilder.buildEnvironmentType(oldEnvironment);
192
193 xmlCursor.beginElement(ENVIRONMENT_QNAME);
194 XmlCursor element = environmentType.newCursor();
195
196 try {
197 element.copyXmlContents(xmlCursor);
198 }
199 finally {
200 element.dispose();
201 }
202 }
203
204 private void moveToFirstStartElement(XmlCursor xmlCursor) throws AssertionError {
205 xmlCursor.toStartDoc();
206 xmlCursor.toFirstChild();
207 while (!xmlCursor.currentTokenType().isStart()) {
208 if (!xmlCursor.toNextSibling()) {
209 break;
210 }
211 }
212 if (!xmlCursor.currentTokenType().isStart()) {
213 throw new AssertionError("Cannot find first start element");
214 }
215 }
216
217 private void convertElement(final XmlCursor cursor, final String namespace) {
218 cursor.push();
219 XmlCursor end = cursor.newCursor();
220
221 try {
222 end.toCursor(cursor);
223 end.toEndToken();
224
225 while (cursor.hasNextToken() && cursor.isLeftOf(end)) {
226 if (cursor.isStart()) {
227 if (!namespace.equals(cursor.getName().getNamespaceURI())) {
228 cursor.setName(new QName(namespace, cursor.getName().getLocalPart()));
229 }
230 }
231
232 cursor.toNextToken();
233 }
234
235 cursor.pop();
236 }
237 finally {
238 end.dispose();
239 }
240 }
241
242 private LinkedHashSet toDependencies() {
243 List artifacts = project.getDependencies();
244 LinkedHashSet dependencies = new LinkedHashSet();
245
246 Iterator iter = artifacts.iterator();
247 while (iter.hasNext()) {
248 Dependency dependency = (Dependency) iter.next();
249
250 //
251 // HACK: Does not appear that we can get the "extention" status of a dependency,
252 // so specifically exclude the ones that we know about, like genesis
253 //
254
255 if (dependency.getGroupId().startsWith("org.apache.geronimo.genesis")) {
256 continue;
257 }
258
259 org.apache.geronimo.kernel.repository.Dependency gdep = toGeronimoDependency(dependency);
260 if (gdep != null) {
261 dependencies.add(gdep);
262 }
263 }
264
265 return dependencies;
266 }
267
268 private static org.apache.geronimo.kernel.repository.Dependency toGeronimoDependency(final Dependency dependency) {
269 Artifact artifact = toGeronimoArtifact(dependency);
270 String type = dependency.getType();
271 String scope = dependency.getScope();
272 String groupId = dependency.getGroupId();
273
274 //!"org.apache.geronimo.specs".equals(groupId) jacc spec needed in plan.xml
275 if ("jar".equalsIgnoreCase(type) && !"junit".equals(groupId) && (scope == null || !scope.equals("provided"))) {
276 if (dependency.getVersion() != null) {
277 artifact = new Artifact(
278 artifact.getGroupId(),
279 artifact.getArtifactId(),
280 dependency.getVersion(),
281 artifact.getType());
282 }
283 return new org.apache.geronimo.kernel.repository.Dependency(artifact, ImportType.CLASSES);
284 }
285 else if ("car".equalsIgnoreCase(type) && "runtime".equalsIgnoreCase(scope)) {
286 return new org.apache.geronimo.kernel.repository.Dependency(artifact, ImportType.CLASSES);
287 }
288 //doesn't work
289 // else if ("car".equalsIgnoreCase(type) && "provided".equalsIgnoreCase(scope)) {
290 // return new org.apache.geronimo.kernel.repository.Dependency(artifact, ImportType.CLASSES);
291 // }
292 else if ("car".equalsIgnoreCase(type) && (scope == null || "compile".equalsIgnoreCase(scope))) { //parent
293 return new org.apache.geronimo.kernel.repository.Dependency(artifact, ImportType.ALL);
294 }
295 else {
296 // not one of ours
297 return null;
298 }
299 }
300
301 private static Artifact toGeronimoArtifact(final Dependency dependency) {
302 String groupId = dependency.getGroupId();
303 String artifactId = dependency.getArtifactId();
304 String version = null;
305 String type = dependency.getType();
306
307 return new Artifact(groupId, artifactId, version, type);
308 }
309
310 interface Inserter {
311 ArtifactType insert(EnvironmentType environmentType);
312 }
313 }