View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.geronimo.plugin.car;
21  
22  import java.io.File;
23  import java.net.URI;
24  
25  import java.util.ArrayList;
26  import java.util.List;
27  import java.util.Iterator;
28  import java.util.Set;
29  import java.util.HashSet;
30  
31  import org.apache.geronimo.deployment.PluginBootstrap2;
32  import org.apache.geronimo.system.configuration.RepositoryConfigurationStore;
33  import org.apache.geronimo.system.repository.Maven2Repository;
34  import org.apache.geronimo.system.resolver.ExplicitDefaultArtifactResolver;
35  import org.apache.geronimo.kernel.Kernel;
36  import org.apache.geronimo.kernel.KernelRegistry;
37  import org.apache.geronimo.kernel.KernelFactory;
38  import org.apache.geronimo.kernel.Naming;
39  import org.apache.geronimo.kernel.repository.DefaultArtifactManager;
40  import org.apache.geronimo.kernel.log.GeronimoLogging;
41  import org.apache.geronimo.kernel.management.State;
42  import org.apache.geronimo.kernel.config.ConfigurationManager;
43  import org.apache.geronimo.kernel.config.ConfigurationUtil;
44  import org.apache.geronimo.kernel.config.ConfigurationData;
45  import org.apache.geronimo.kernel.config.KernelConfigurationManager;
46  import org.apache.geronimo.gbean.AbstractName;
47  import org.apache.geronimo.gbean.GBeanData;
48  import org.apache.geronimo.gbean.GBeanInfo;
49  import org.apache.geronimo.gbean.ReferencePatterns;
50  import org.apache.geronimo.gbean.AbstractNameQuery;
51  
52  import org.apache.geronimo.genesis.ArtifactItem;
53  
54  import org.apache.maven.archiver.MavenArchiveConfiguration;
55  import org.apache.maven.archiver.MavenArchiver;
56  import org.apache.maven.plugin.MojoExecutionException;
57  import org.apache.maven.artifact.Artifact;
58  
59  import org.codehaus.plexus.util.FileUtils;
60  import org.codehaus.plexus.archiver.jar.JarArchiver;
61  
62  /**
63   * Build a Geronimo Configuration using the local Maven infrastructure.
64   *
65   * <p>
66   * <b>NOTE:</b> Calling pom.xml must have defined a ${geronimoVersion} property.
67   * </p>
68   *
69   * @goal package
70   * @requiresDependencyResolution runtime
71   *
72   * @version $Rev: 451661 $ $Date: 2006-09-30 13:45:53 -0700 (Sat, 30 Sep 2006) $
73   */
74  public class PackageMojo
75      extends AbstractCarMojo
76  {
77      /**
78       * The maven archive configuration to use.
79       *
80       * See <a href="http://maven.apache.org/ref/current/maven-archiver/apidocs/org/apache/maven/archiver/MavenArchiveConfiguration.html">the Javadocs for MavenArchiveConfiguration</a>.
81       *
82       * @parameter
83       */
84      private MavenArchiveConfiguration archive = new MavenArchiveConfiguration();
85  
86      /**
87       * The Jar archiver.
88       *
89       * @parameter expression="${component.org.codehaus.plexus.archiver.Archiver#jar}"
90       * @required
91       * @readonly
92       */
93      private JarArchiver jarArchiver = null;
94  
95      /**
96       * Directory containing the generated archive.
97       *
98       * @parameter expression="${project.build.directory}"
99       * @required
100      */
101     private File outputDirectory = null;
102 
103     /**
104      * Directory containing the classes/resources.
105      *
106      * @parameter expression="${project.build.outputDirectory}"
107      * @required
108      */
109     private File classesDirectory = null;
110 
111     /**
112      * Name of the generated archive.
113      *
114      * @parameter expression="${project.build.finalName}"
115      * @required
116      */
117     private String finalName = null;
118 
119     /**
120      * ???
121      *
122      * @parameter expression="${settings.localRepository}"
123      * @required
124      * @readonly
125      */
126     private File repository = null;
127 
128     /**
129      * ???
130      *
131      * @parameter expression="${project.build.directory}/repository"
132      * @required
133      */
134     private File targetRepository = null;
135 
136     /**
137      * ???
138      *
139      * @parameter expression="org.apache.geronimo.configs/geronimo-gbean-deployer/${geronimoVersion}/car"
140      * @required
141      * @readonly
142      */
143     private String deafultDeploymentConfig = null;
144 
145     /**
146      * ???
147      *
148      * @parameter
149      */
150     private List deploymentConfigs;
151 
152     /**
153      * The name of the deployer which will be used to deploy the CAR.
154      *
155      * @parameter expression="org.apache.geronimo.configs/geronimo-gbean-deployer/${geronimoVersion}/car?j2eeType=Deployer,name=Deployer"
156      * @required
157      */
158     private String deployerName = null;
159 
160     /**
161      * The plan file for the CAR.
162      *
163      * @parameter expression="${project.build.directory}/plan/plan.xml"
164      * @required
165      */
166     private File planFile = null;
167 
168     /**
169      * The file to include as a module of the CAR.
170      *
171      * @parameter
172      */
173     private File moduleFile = null;
174 
175     /**
176      * An {@link ArtifactItem} to include as a module of the CAR.
177      *
178      * @parameter
179      */
180     private ArtifactItem module = null;
181 
182     /**
183      * The location where the properties mapping will be generated.
184      *
185      * <p>
186      * Probably don't wanto to change this.
187      *
188      * @parameter expression="${project.build.directory}/explicit-versions.properties"
189      */
190     private File explicitResolutionProperties = null;
191 
192     /**
193      * An array of {@link ClasspathElement} objects which will be used to construct the
194      * Class-Path entry of the manifest.
195      *
196      * This is needed to allow per-element prefixes to be added, which the standard Maven archiver
197      * does not provide.
198      *
199      * @parameter
200      */
201     private ClasspathElement[] classpath = null;
202 
203     /**
204      * The default prefix to be applied to all elements of the <tt>classpath</tt> which
205      * do not provide a prefix.
206      *
207      * @parameter
208      */
209     private String classpathPrefix = null;
210 
211     /**
212      * True to enable the bootshell when packaging.
213      *
214      * @parameter
215      */
216     private boolean bootstrap = false;
217 
218     //
219     // Mojo
220     //
221 
222     protected void doExecute() throws Exception {
223         // We need to make sure to clean up any previous work first or this operation will fail
224         FileUtils.forceDelete(targetRepository);
225         FileUtils.forceMkdir(targetRepository);
226 
227         // Use the default configs if none specified
228         if (deploymentConfigs == null) {
229             deploymentConfigs = new ArrayList();
230             deploymentConfigs.add(deafultDeploymentConfig);
231         }
232         log.debug("Deployment configs: " + deploymentConfigs);
233 
234         // If module is set, then resolve the artifact and set moduleFile
235         if (module != null) {
236             Artifact artifact = getArtifact(module);
237             moduleFile = artifact.getFile();
238             log.debug("Using module file: " + moduleFile);
239         }
240 
241         generateExplicitVersionProperties(explicitResolutionProperties);
242 
243         if (bootstrap) {
244             executeBootShell();
245         }
246         else {
247             buildPackage();
248         }
249 
250         // Build the archive
251         File archive = createArchive();
252 
253         // Attach the generated archive for install/deploy
254         project.getArtifact().setFile(archive);
255     }
256 
257     private File getArtifactInRepositoryDir() {
258         //
259         // HACK: Generate the filename in the repo... really should delegate this to the repo impl
260         //
261 
262         File dir = new File(targetRepository, project.getGroupId().replace('.', '/'));
263         dir = new File(dir, project.getArtifactId());
264         dir = new File(dir, project.getVersion());
265         dir = new File(dir, project.getArtifactId() + "-" + project.getVersion() + ".car");
266 
267         return dir;
268     }
269 
270     public void executeBootShell() throws Exception {
271         log.debug("Starting bootstrap shell...");
272 
273         PluginBootstrap2 boot = new PluginBootstrap2();
274 
275         boot.setBuildDir(outputDirectory);
276         boot.setCarFile(getArtifactInRepositoryDir());
277         boot.setLocalRepo(repository);
278         boot.setPlan(planFile);
279 
280         // Generate expanded so we can use Maven to generate the archive
281         boot.setExpanded(true);
282 
283         boot.bootstrap();
284     }
285 
286     /**
287      * Generates the configuration archive.
288      */
289     private File createArchive() throws MojoExecutionException {
290         File archiveFile = getArchiveFile(outputDirectory, finalName, null);
291 
292         MavenArchiver archiver = new MavenArchiver();
293         archiver.setArchiver(jarArchiver);
294         archiver.setOutputFile(archiveFile);
295 
296         try {
297             // Incldue the generated artifact contents
298             archiver.getArchiver().addDirectory(getArtifactInRepositoryDir());
299 
300             // Include the optional classes.resources
301             if (classesDirectory.isDirectory()) {
302                 archiver.getArchiver().addDirectory(classesDirectory);
303             }
304 
305             if (classpath != null) {
306                 archive.addManifestEntry("Class-Path", getClassPath());
307             }
308 
309             archiver.createArchive(project, archive);
310 
311             return archiveFile;
312         }
313         catch (Exception e) {
314             throw new MojoExecutionException("Failed to create archive", e);
315         }
316     }
317 
318     private String getClassPath() throws MojoExecutionException {
319         StringBuffer buff = new StringBuffer();
320 
321         for (int i=0; i < classpath.length; i++) {
322             Artifact artifact = getArtifact(classpath[i]);
323 
324             //
325             // TODO: Need to optionally get all transitive dependencies... but dunno how to get that intel from m2
326             //
327 
328             String prefix = classpath[i].getClasspathPrefix();
329             if (prefix == null) {
330                 prefix = classpathPrefix;
331             }
332 
333             if (prefix != null) {
334                 buff.append(prefix);
335 
336                 if (!prefix.endsWith("/")) {
337                     buff.append("/");
338                 }
339             }
340 
341             File file = artifact.getFile();
342             buff.append(file.getName());
343 
344             if (i + 1< classpath.length) {
345                 buff.append(" ");
346             }
347         }
348 
349         log.debug("Using classpath: " + buff);
350 
351         return buff.toString();
352     }
353 
354     //
355     // Deployment
356     //
357 
358     private static final String KERNEL_NAME = "geronimo.maven";
359 
360     /**
361      * Reference to the kernel that will last the lifetime of this classloader.
362      * The KernelRegistry keeps soft references that may be garbage collected.
363      */
364     private static Kernel kernel;
365 
366     private static AbstractName targetConfigStoreAName;
367 
368     private static AbstractName targetRepositoryAName;
369 
370     private boolean targetSet;
371 
372     public void buildPackage() throws Exception {
373         log.info("Packaging module configuration: " + planFile);
374 
375 
376         Kernel kernel = createKernel();
377         if (!targetSet) {
378             kernel.stopGBean(targetRepositoryAName);
379             kernel.setAttribute(targetRepositoryAName, "root", targetRepository.toURI());
380             kernel.startGBean(targetRepositoryAName);
381 
382             if (kernel.getGBeanState(targetConfigStoreAName) != State.RUNNING_INDEX) {
383                 throw new IllegalStateException("After restarted repository then config store is not running");
384             }
385 
386             targetSet = true;
387         }
388 
389         log.debug("Starting configuration...");
390 
391         // start the Configuration we're going to use for this deployment
392         ConfigurationManager configurationManager = ConfigurationUtil.getConfigurationManager(kernel);
393         try {
394             for (Iterator iterator = deploymentConfigs.iterator(); iterator.hasNext();) {
395                 String artifactName = (String) iterator.next();
396                 org.apache.geronimo.kernel.repository.Artifact configName =
397                         org.apache.geronimo.kernel.repository.Artifact.create(artifactName);
398                 if (!configurationManager.isLoaded(configName)) {
399                     configurationManager.loadConfiguration(configName);
400                     configurationManager.startConfiguration(configName);
401                 }
402             }
403         } finally {
404             ConfigurationUtil.releaseConfigurationManager(kernel, configurationManager);
405         }
406 
407         log.debug("Deploying...");
408 
409         AbstractName deployer = locateDeployer(kernel);
410         invokeDeployer(kernel, deployer, targetConfigStoreAName.toString());
411     }
412 
413     /**
414      * Create a Geronimo Kernel to contain the deployment configurations.
415      */
416     private synchronized Kernel createKernel() throws Exception {
417         // first return our cached version
418         if (kernel != null) {
419             return kernel;
420         }
421 
422         log.debug("Creating kernel...");
423 
424         // check the registry in case someone else created one
425         kernel = KernelRegistry.getKernel(KERNEL_NAME);
426         if (kernel != null) {
427             return kernel;
428         }
429 
430         GeronimoLogging geronimoLogging = GeronimoLogging.getGeronimoLogging("WARN");
431         if (geronimoLogging == null) {
432             geronimoLogging = GeronimoLogging.DEBUG;
433         }
434         GeronimoLogging.initialize(geronimoLogging);
435 
436         // boot one ourselves
437         kernel = KernelFactory.newInstance().createKernel(KERNEL_NAME);
438         kernel.boot();
439 
440         bootDeployerSystem();
441 
442         return kernel;
443     }
444 
445     /**
446      * Boot the in-Maven deployment system.
447      *
448      * <p>
449      * This contains Repository and ConfigurationStore GBeans that map to
450      * the local maven installation.
451      */
452     private void bootDeployerSystem() throws Exception {
453         log.debug("Booting deployer system...");
454 
455         org.apache.geronimo.kernel.repository.Artifact baseId =
456                 new org.apache.geronimo.kernel.repository.Artifact("geronimo", "packaging", "fixed", "car");
457         Naming naming = kernel.getNaming();
458         ConfigurationData bootstrap = new ConfigurationData(baseId, naming);
459         ClassLoader cl = getClass().getClassLoader();
460         Set repoNames = new HashSet();
461 
462         // Source repo
463         GBeanData repoGBean = bootstrap.addGBean("SourceRepository", GBeanInfo.getGBeanInfo(Maven2Repository.class.getName(), cl));
464         URI repositoryURI = repository.toURI();
465         repoGBean.setAttribute("root", repositoryURI);
466         repoNames.add(repoGBean.getAbstractName());
467 
468         // Target repo
469         GBeanData targetRepoGBean = bootstrap.addGBean("TargetRepository", GBeanInfo.getGBeanInfo(Maven2Repository.class.getName(), cl));
470         URI targetRepositoryURI = targetRepository.toURI();
471         targetRepoGBean.setAttribute("root", targetRepositoryURI);
472         repoNames.add(targetRepoGBean.getAbstractName());
473         targetRepositoryAName = targetRepoGBean.getAbstractName();
474 
475         GBeanData artifactManagerGBean = bootstrap.addGBean("ArtifactManager", DefaultArtifactManager.GBEAN_INFO);
476         GBeanData artifactResolverGBean = bootstrap.addGBean("ArtifactResolver", ExplicitDefaultArtifactResolver.GBEAN_INFO);
477         artifactResolverGBean.setAttribute("versionMapLocation", explicitResolutionProperties.getAbsolutePath());
478         ReferencePatterns repoPatterns = new ReferencePatterns(repoNames);
479         artifactResolverGBean.setReferencePatterns("Repositories", repoPatterns);
480         artifactResolverGBean.setReferencePattern("ArtifactManager", artifactManagerGBean.getAbstractName());
481 
482         Set storeNames = new HashSet();
483 
484         // Source config store
485         GBeanInfo configStoreInfo = GBeanInfo.getGBeanInfo(MavenConfigStore.class.getName(), cl);
486         GBeanData storeGBean = bootstrap.addGBean("ConfigStore", configStoreInfo);
487         if (configStoreInfo.getReference("Repository") != null) {
488             storeGBean.setReferencePattern("Repository", repoGBean.getAbstractName());
489         }
490         storeNames.add(storeGBean.getAbstractName());
491 
492         // Target config store
493         GBeanInfo targetConfigStoreInfo = GBeanInfo.getGBeanInfo(RepositoryConfigurationStore.class.getName(), cl);
494         GBeanData targetStoreGBean = bootstrap.addGBean("TargetConfigStore", targetConfigStoreInfo);
495         if (targetConfigStoreInfo.getReference("Repository") != null) {
496             targetStoreGBean.setReferencePattern("Repository", targetRepoGBean.getAbstractName());
497         }
498         storeNames.add(targetStoreGBean.getAbstractName());
499 
500         targetConfigStoreAName = targetStoreGBean.getAbstractName();
501         targetSet = true;
502 
503         GBeanData attrManagerGBean = bootstrap.addGBean("AttributeStore", MavenAttributeStore.GBEAN_INFO);
504         GBeanData configManagerGBean = bootstrap.addGBean("ConfigManager", KernelConfigurationManager.GBEAN_INFO);
505         configManagerGBean.setReferencePatterns("Stores", new ReferencePatterns(storeNames));
506         configManagerGBean.setReferencePattern("AttributeStore", attrManagerGBean.getAbstractName());
507         configManagerGBean.setReferencePattern("ArtifactManager", artifactManagerGBean.getAbstractName());
508         configManagerGBean.setReferencePattern("ArtifactResolver", artifactResolverGBean.getAbstractName());
509         configManagerGBean.setReferencePatterns("Repositories", repoPatterns);
510 
511         ConfigurationUtil.loadBootstrapConfiguration(kernel, bootstrap, cl);
512     }
513 
514     /**
515      * Locate a Deployer GBean matching the deployerName pattern.
516      *
517      * @param kernel the kernel to search.
518      * @return the ObjectName of the Deployer GBean
519      *
520      * @throws IllegalStateException if there is not exactly one GBean matching the deployerName pattern
521      */
522     private AbstractName locateDeployer(final Kernel kernel) {
523         AbstractName name = new AbstractName(URI.create(deployerName));
524 
525         Iterator i = kernel.listGBeans(new AbstractNameQuery(name)).iterator();
526         if (!i.hasNext()) {
527             throw new IllegalStateException("No deployer found matching deployerName: " + name);
528         }
529 
530         AbstractName deployer = (AbstractName)i.next();
531         if (i.hasNext()) {
532             throw new IllegalStateException("Multiple deployers found matching deployerName: " + name);
533         }
534 
535         return deployer;
536     }
537 
538     private static final String[] DEPLOY_SIGNATURE = {
539         boolean.class.getName(),
540         File.class.getName(),
541         File.class.getName(),
542         File.class.getName(),
543         Boolean.TYPE.getName(),
544         String.class.getName(),
545         String.class.getName(),
546         String.class.getName(),
547         String.class.getName(),
548         String.class.getName(),
549         String.class.getName(),
550         String.class.getName(),
551         String.class.getName(),
552     };
553 
554     private List invokeDeployer(final Kernel kernel, final AbstractName deployer, final String targetConfigStore) throws Exception {
555         Object[] args = {
556             Boolean.FALSE, // Not in-place
557             planFile,
558             moduleFile,
559             null, // Target file
560             Boolean.TRUE, // Install
561             null, // main-class
562             null, // main-gbean
563             null, // main-method
564             null, // Manifest configurations
565             null, // class-path
566             null, // endorsed-dirs
567             null, // extension-dirs
568             targetConfigStore
569         };
570 
571         return (List) kernel.invoke(deployer, "deploy", args, DEPLOY_SIGNATURE);
572     }
573 }