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    }