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    import java.util.Collections;
025    import java.util.LinkedHashSet;
026    import java.util.List;
027    import java.util.Properties;
028    
029    import javax.xml.namespace.QName;
030    
031    import org.apache.geronimo.deployment.service.EnvironmentBuilder;
032    import org.apache.geronimo.deployment.xbeans.ArtifactType;
033    import org.apache.geronimo.deployment.xbeans.EnvironmentType;
034    import org.apache.geronimo.kernel.repository.Artifact;
035    import org.apache.geronimo.kernel.repository.Environment;
036    import org.apache.geronimo.kernel.repository.ImportType;
037    import org.apache.velocity.Template;
038    import org.apache.velocity.VelocityContext;
039    import org.apache.velocity.app.Velocity;
040    import org.apache.velocity.app.VelocityEngine;
041    import org.apache.xmlbeans.XmlCursor;
042    import org.apache.xmlbeans.XmlObject;
043    import org.apache.xmlbeans.XmlOptions;
044    
045    //
046    // TODO: Rename to PreparePlanMojo
047    //
048    
049    /**
050     * Add module id and dependencies to a plan and process with velocity
051     *
052     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
053     * @goal prepare-plan
054     */
055    public class PlanProcessorMojo
056            extends AbstractCarMojo {
057        private static final String ENVIRONMENT_LOCAL_NAME = "environment";
058    
059        private static final QName ENVIRONMENT_QNAME = new QName("http://geronimo.apache.org/xml/ns/deployment-1.2", "environment");
060    
061        /**
062         * Location of unproccesed plan, normally missing moduleId and dependencies.
063         *
064         * @parameter expression="${basedir}/src/main/plan"
065         * @required
066         */
067        protected File sourceDir = null;
068    
069        /**
070         * Directory to put the processed plan in.
071         *
072         * @parameter expression="${project.build.directory}/resources/META-INF"
073         * @required
074         */
075        protected File targetDir = null;
076    
077        /**
078         * Name of the unprocessed source and processed target plan file.
079         *
080         * @parameter default-value="plan.xml"
081         * @required
082         */
083        protected String planFileName = null;
084    
085        /**
086         * XXX
087         *
088         * @parameter expression="${project.build.directory}/resources/META-INF/plan.xml"
089         * @required
090         */
091        protected File targetFile = null;
092    
093        /**
094         * Dependencies explicitly listed in the car-maven-plugin configuration
095         *
096         * @parameter
097         */
098        private List<Dependency> dependencies = Collections.emptyList();
099    
100        /**
101         * Configuration of use of maven dependencies.  If missing or if value element is false, use the explicit list in the car-maven-plugin configuration.
102         * If present and true, use the maven dependencies in the current pom file of scope null, runtime, or compile.  In addition, the version of the maven
103         * dependency can be included or not depending on the includeVersion element.
104         *
105         * @parameter
106         */
107        private UseMavenDependencies useMavenDependencies;
108    
109        private VelocityContext createContext() {
110            VelocityContext context = new VelocityContext();
111    
112            // Load properties, It inherits them all!
113            Properties props = project.getProperties();
114            for (Object o : props.keySet()) {
115                String key = (String) o;
116                String value = props.getProperty(key);
117    
118                log.debug("Setting " + key + "=" + value);
119                context.put(key, value);
120            }
121    
122            context.put("pom", project);
123    
124            return context;
125        }
126    
127        protected void doExecute() throws Exception {
128            //
129            // FIXME: Do not need velocity here, we only need to filter,
130            //        could use resources plugin to do this for us, or
131            //        implement what resources plugin does here
132            //
133            //        Also velocity does not handle property expansion of expressions like
134            //        ${foo.bar} to the value of the "foo.bar" property :-(
135            //
136            //        Might be better of just hand rolling something...
137            //
138    
139            VelocityContext context = createContext();
140    
141            VelocityEngine velocity = new VelocityEngine();
142            velocity.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, sourceDir.getAbsolutePath());
143    
144            // Don't spit out any logs
145            velocity.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM_CLASS, "org.apache.velocity.runtime.log.NullLogSystem");
146            velocity.init();
147    
148            Template template = velocity.getTemplate(planFileName);
149            StringWriter writer = new StringWriter();
150            template.merge(context, writer);
151    
152            String plan = writer.toString();
153    
154            XmlObject doc = XmlObject.Factory.parse(plan);
155            XmlCursor xmlCursor = doc.newCursor();
156            LinkedHashSet<org.apache.geronimo.kernel.repository.Dependency> dependencies = toDependencies();
157            Artifact configId = new Artifact(project.getGroupId(), project.getArtifactId(), project.getVersion(), "car");
158    
159            try {
160                mergeEnvironment(xmlCursor, configId, dependencies);
161    
162                if (targetDir.exists()) {
163                    if (!targetDir.isDirectory()) {
164                        throw new RuntimeException("TargetDir: " + this.targetDir + " exists and is not a directory");
165                    }
166                } else {
167                    targetDir.mkdirs();
168                }
169    
170                XmlOptions xmlOptions = new XmlOptions();
171                xmlOptions.setSavePrettyPrint();
172                doc.save(targetFile, xmlOptions);
173    
174                log.info("Generated: " + targetFile);
175            }
176            finally {
177                xmlCursor.dispose();
178            }
179        }
180    
181        void mergeEnvironment(final XmlCursor xmlCursor, final Artifact configId, final LinkedHashSet<org.apache.geronimo.kernel.repository.Dependency> dependencies) {
182            moveToFirstStartElement(xmlCursor);
183    
184            boolean atLeastOneChild = xmlCursor.toFirstChild();
185            if (!atLeastOneChild) {
186                // this is an empty element. Move to EndToken such XmlCursor.beginElement inserts an element inside it.
187                xmlCursor.toEndToken();
188            }
189            QName childName = xmlCursor.getName();
190            Environment oldEnvironment;
191    
192            if (childName != null && childName.getLocalPart().equals(ENVIRONMENT_LOCAL_NAME)) {
193                convertElement(xmlCursor, ENVIRONMENT_QNAME.getNamespaceURI());
194                XmlObject xmlObject = xmlCursor.getObject();
195                EnvironmentType environmentType = (EnvironmentType) xmlObject.copy().changeType(EnvironmentType.type);
196                oldEnvironment = EnvironmentBuilder.buildEnvironment(environmentType);
197                xmlCursor.removeXml();
198            } else {
199                oldEnvironment = new Environment();
200            }
201    
202            Environment newEnvironment = new Environment();
203            newEnvironment.setConfigId(configId);
204            newEnvironment.setDependencies(dependencies);
205    
206            EnvironmentBuilder.mergeEnvironments(oldEnvironment, newEnvironment);
207            EnvironmentType environmentType = EnvironmentBuilder.buildEnvironmentType(oldEnvironment);
208    
209            xmlCursor.beginElement(ENVIRONMENT_QNAME);
210            XmlCursor element = environmentType.newCursor();
211    
212            try {
213                element.copyXmlContents(xmlCursor);
214            }
215            finally {
216                element.dispose();
217            }
218        }
219    
220        private void moveToFirstStartElement(XmlCursor xmlCursor) throws AssertionError {
221            xmlCursor.toStartDoc();
222            xmlCursor.toFirstChild();
223            while (!xmlCursor.currentTokenType().isStart()) {
224                if (!xmlCursor.toNextSibling()) {
225                    break;
226                }
227            }
228            if (!xmlCursor.currentTokenType().isStart()) {
229                throw new AssertionError("Cannot find first start element");
230            }
231        }
232    
233        private void convertElement(final XmlCursor cursor, final String namespace) {
234            cursor.push();
235            XmlCursor end = cursor.newCursor();
236    
237            try {
238                end.toCursor(cursor);
239                end.toEndToken();
240    
241                while (cursor.hasNextToken() && cursor.isLeftOf(end)) {
242                    if (cursor.isStart()) {
243                        if (!namespace.equals(cursor.getName().getNamespaceURI())) {
244                            cursor.setName(new QName(namespace, cursor.getName().getLocalPart()));
245                        }
246                    }
247    
248                    cursor.toNextToken();
249                }
250    
251                cursor.pop();
252            }
253            finally {
254                end.dispose();
255            }
256        }
257    
258        private LinkedHashSet<org.apache.geronimo.kernel.repository.Dependency> toDependencies() {
259            LinkedHashSet<org.apache.geronimo.kernel.repository.Dependency> dependencies = new LinkedHashSet<org.apache.geronimo.kernel.repository.Dependency>();
260    
261            if (useMavenDependencies == null || !useMavenDependencies.isValue()) {
262                for (Dependency dependency : this.dependencies) {
263                    org.apache.geronimo.kernel.repository.Dependency gdep = dependency.toDependency();
264                    dependencies.add(gdep);
265                }
266            } else {
267                List<org.apache.maven.model.Dependency> includedDependencies = project.getOriginalModel().getDependencies();
268                List<org.apache.maven.model.Dependency> artifacts = project.getDependencies();
269                for (org.apache.maven.model.Dependency dependency : includedDependencies) {
270                    dependency = resolveDependency(dependency, artifacts);
271                    if (includeDependency(dependency)) {
272                        org.apache.geronimo.kernel.repository.Dependency gdep = toGeronimoDependency(dependency, useMavenDependencies.isIncludeVersion());
273                        dependencies.add(gdep);
274                    }
275                }
276            }
277    
278            return dependencies;
279        }
280    
281    
282        private static org.apache.geronimo.kernel.repository.Dependency toGeronimoDependency(final org.apache.maven.model.Dependency dependency, boolean includeVersion) {
283            Artifact artifact = toGeronimoArtifact(dependency, includeVersion);
284            return new org.apache.geronimo.kernel.repository.Dependency(artifact, ImportType.ALL);
285        }
286    
287        private static Artifact toGeronimoArtifact(final org.apache.maven.model.Dependency dependency, boolean includeVersion) {
288            String groupId = dependency.getGroupId();
289            String artifactId = dependency.getArtifactId();
290            String version = includeVersion ? dependency.getVersion() : null;
291            String type = dependency.getType();
292    
293            return new Artifact(groupId, artifactId, version, type);
294        }
295    
296    
297        interface Inserter {
298            ArtifactType insert(EnvironmentType environmentType);
299        }
300    }