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: */package org.apache.geronimo.mavenplugins.car;
019:
020: import java.io.File;
021: import java.net.URI;
022: import java.util.ArrayList;
023: import java.util.Arrays;
024: import java.util.HashMap;
025: import java.util.HashSet;
026: import java.util.Iterator;
027: import java.util.List;
028: import java.util.Set;
029:
030: import org.apache.geronimo.deployment.PluginBootstrap2;
031: import org.apache.geronimo.gbean.AbstractName;
032: import org.apache.geronimo.gbean.AbstractNameQuery;
033: import org.apache.geronimo.gbean.GBeanData;
034: import org.apache.geronimo.gbean.GBeanInfo;
035: import org.apache.geronimo.gbean.ReferencePatterns;
036: import org.apache.geronimo.kernel.Kernel;
037: import org.apache.geronimo.kernel.KernelFactory;
038: import org.apache.geronimo.kernel.KernelRegistry;
039: import org.apache.geronimo.kernel.Naming;
040: import org.apache.geronimo.kernel.config.ConfigurationData;
041: import org.apache.geronimo.kernel.config.ConfigurationManager;
042: import org.apache.geronimo.kernel.config.ConfigurationUtil;
043: import org.apache.geronimo.kernel.config.KernelConfigurationManager;
044: import org.apache.geronimo.kernel.config.LifecycleException;
045: import org.apache.geronimo.kernel.config.RecordingLifecycleMonitor;
046: import org.apache.geronimo.kernel.log.GeronimoLogging;
047: import org.apache.geronimo.kernel.management.State;
048: import org.apache.geronimo.kernel.repository.DefaultArtifactManager;
049: import org.apache.geronimo.system.configuration.RepositoryConfigurationStore;
050: import org.apache.geronimo.system.repository.Maven2Repository;
051: import org.apache.geronimo.system.resolver.ExplicitDefaultArtifactResolver;
052: import org.apache.maven.archiver.MavenArchiveConfiguration;
053: import org.apache.maven.artifact.Artifact;
054: import org.apache.maven.artifact.factory.ArtifactFactory;
055: import org.apache.maven.project.MavenProject;
056: import org.codehaus.mojo.pluginsupport.dependency.DependencyTree;
057: import org.codehaus.mojo.pluginsupport.util.ArtifactItem;
058: import org.codehaus.plexus.archiver.jar.JarArchiver;
059: import org.codehaus.plexus.util.FileUtils;
060:
061: /**
062: * Build a Geronimo Configuration using the local Maven infrastructure.
063: *
064: * @goal package
065: * @requiresDependencyResolution runtime
066: *
067: * @version $Rev: 617364 $ $Date: 2008-01-31 23:44:42 -0800 (Thu, 31 Jan 2008) $
068: */
069: public class PackageMojo extends AbstractCarMojo {
070:
071: /**
072: * @component
073: * @required
074: * @readonly
075: */
076: private ArtifactFactory artifactFactory;
077:
078: /**
079: * Directory containing the generated archive.
080: *
081: * @parameter expression="${project.build.directory}"
082: * @required
083: */
084: private File outputDirectory = null;
085:
086: /**
087: * The local Maven repository which will be used to pull artifacts into the Geronimo repository when packaging.
088: *
089: * @parameter expression="${settings.localRepository}"
090: * @required
091: * @readonly
092: */
093: private File repository = null;
094:
095: /**
096: * The Geronimo repository where modules will be packaged up from.
097: *
098: * @parameter expression="${project.build.directory}/repository"
099: * @required
100: */
101: private File targetRepository = null;
102:
103: /**
104: * The default deployer module to be used when no other deployer modules are configured.
105: *
106: * @parameter expression="org.apache.geronimo.framework/geronimo-gbean-deployer/${geronimoVersion}/car"
107: * @required
108: * @readonly
109: */
110: private String defaultDeploymentConfig = null;
111:
112: /**
113: * Ther deployer modules to be used when packaging.
114: *
115: * @parameter
116: */
117: private String[] deploymentConfigs;
118:
119: /**
120: * The name of the deployer which will be used to deploy the CAR.
121: *
122: * @parameter expression="org.apache.geronimo.framework/geronimo-gbean-deployer/${geronimoVersion}/car?j2eeType=Deployer,name=Deployer"
123: * @required
124: */
125: private String deployerName = null;
126:
127: /**
128: * The plan file for the CAR.
129: *
130: * @parameter expression="${project.build.directory}/resources/META-INF/plan.xml"
131: * @required
132: */
133: private File planFile = null;
134:
135: /**
136: * The file to include as a module of the CAR.
137: *
138: * @parameter
139: */
140: private File moduleFile = null;
141:
142: /**
143: * An {@link ArtifactItem} to include as a module of the CAR.
144: *
145: * @parameter
146: */
147: private ArtifactItem module = null;
148:
149: /**
150: * The location where the properties mapping will be generated.
151: *
152: * <p>
153: * Probably don't want to change this.
154: * </p>
155: *
156: * @parameter expression="${project.build.directory}/explicit-versions.properties"
157: */
158: private File explicitResolutionProperties = null;
159:
160: /**
161: * True to enable the bootshell when packaging.
162: *
163: * @parameter
164: */
165: private boolean bootstrap = false;
166:
167: /**
168: * Holds a local repo lookup instance so that we can use the current project to resolve.
169: * This is required since the Kernel used to deploy is cached.
170: */
171: private static ThreadLocal<Maven2RepositoryAdapter.ArtifactLookup> lookupHolder = new ThreadLocal<Maven2RepositoryAdapter.ArtifactLookup>();
172:
173: //
174: // Mojo
175: //
176:
177: protected void doExecute() throws Exception {
178: // We need to make sure to clean up any previous work first or this operation will fail
179: FileUtils.forceDelete(targetRepository);
180: FileUtils.forceMkdir(targetRepository);
181:
182: // Use the default configs if none specified
183: if (deploymentConfigs == null) {
184: if (!bootstrap) {
185: deploymentConfigs = new String[] { defaultDeploymentConfig };
186: } else {
187: deploymentConfigs = new String[] {};
188: }
189: }
190: log.debug("Deployment configs: "
191: + Arrays.asList(deploymentConfigs));
192:
193: //
194: // NOTE: Resolve deployment modules, this is needed to ensure that the proper artifacts are in the
195: // local repository to perform deployment. If the deployer modules (or their dependencies)
196: // are missing from the source respository, then strange packaging failures will occur.
197: //
198: Set<Artifact> additionalArtifacts = new HashSet<Artifact>();
199: for (String deploymentConfig : deploymentConfigs) {
200: Artifact artifact = geronimoToMavenArtifact(org.apache.geronimo.kernel.repository.Artifact
201: .create(deploymentConfig));
202: log.debug("Resolving deployer module: " + artifact);
203: Artifact resolved = resolveArtifact(artifact, true);
204: additionalArtifacts.add(resolved);
205: }
206: //Ensure that these dependencies are available to geronimo
207: if (project.getDependencyArtifacts() == null) {
208: Set<Artifact> oldArtifacts = project.createArtifacts(
209: dependencyHelper.getArtifactFactory(), null, null);
210: additionalArtifacts.addAll(oldArtifacts);
211: } else {
212: Set<Artifact> oldArtifacts = project
213: .getDependencyArtifacts();
214: additionalArtifacts.addAll(oldArtifacts);
215: }
216: project.setDependencyArtifacts(additionalArtifacts);
217:
218: // If module is set, then resolve the artifact and set moduleFile
219: if (module != null) {
220: Artifact artifact = getArtifact(module);
221: moduleFile = artifact.getFile();
222: log.debug("Using module file: " + moduleFile);
223: }
224:
225: MavenProject depsProject = new MavenProject(project);
226: List<org.apache.maven.model.Dependency> projectDeps = new ArrayList<org.apache.maven.model.Dependency>();
227: for (org.apache.maven.model.Dependency dep : (List<org.apache.maven.model.Dependency>) project
228: .getDependencies()) {
229: org.apache.maven.model.Dependency newDep = new org.apache.maven.model.Dependency();
230: newDep.setArtifactId(dep.getArtifactId());
231: newDep.setGroupId(dep.getGroupId());
232: newDep.setClassifier(dep.getClassifier());
233: newDep.setExclusions(dep.getExclusions());
234: newDep.setOptional(dep.isOptional());
235: newDep.setSystemPath(dep.getSystemPath());
236: newDep.setType(dep.getType());
237: newDep.setVersion(dep.getVersion());
238: // don't copy scope
239:
240: projectDeps.add(newDep);
241: }
242: depsProject.setDependencies(projectDeps);
243: depsProject.setDependencyArtifacts(depsProject.createArtifacts(
244: artifactFactory, null, null));
245: dependencies.setRootNode(dependencyHelper.getDependencies(
246: depsProject).getRootNode());
247:
248: generateExplicitVersionProperties(explicitResolutionProperties,
249: dependencies);
250:
251: //
252: // NOTE: Install a local lookup, so that the cached kernel can resolve based on the current project
253: // and not the project where the kernel was first initialized.
254: //
255: lookupHolder.set(new ArtifactLookupImpl(new HashMap()));
256:
257: if (bootstrap) {
258: executeBootShell();
259: } else {
260: buildPackage();
261: }
262: }
263:
264: private File getArtifactInRepositoryDir() {
265: //
266: // HACK: Generate the filename in the repo... really should delegate this to the repo impl
267: //
268:
269: File dir = new File(targetRepository, project.getGroupId()
270: .replace('.', '/'));
271: dir = new File(dir, project.getArtifactId());
272: dir = new File(dir, project.getVersion());
273: dir = new File(dir, project.getArtifactId() + "-"
274: + project.getVersion() + ".car");
275:
276: return dir;
277: }
278:
279: public void executeBootShell() throws Exception {
280: log.debug("Starting bootstrap shell...");
281:
282: PluginBootstrap2 boot = new PluginBootstrap2();
283:
284: boot.setBuildDir(outputDirectory);
285: boot.setCarFile(getArtifactInRepositoryDir());
286: boot.setLocalRepo(repository);
287: boot.setPlan(planFile);
288:
289: // Generate expanded so we can use Maven to generate the archive
290: boot.setExpanded(true);
291:
292: boot.bootstrap();
293: }
294:
295: //
296: // Deployment
297: //
298:
299: private static final String KERNEL_NAME = "geronimo.maven";
300:
301: /**
302: * Reference to the kernel that will last the lifetime of this classloader.
303: * The KernelRegistry keeps soft references that may be garbage collected.
304: */
305: private Kernel kernel;
306:
307: private AbstractName targetConfigStoreAName;
308:
309: private AbstractName targetRepositoryAName;
310:
311: private boolean targetSet;
312:
313: private DependencyTree dependencies = new DependencyTree();
314:
315: public void buildPackage() throws Exception {
316: log.info("Packaging module configuration: " + planFile);
317:
318: Kernel kernel = createKernel();
319: if (!targetSet) {
320: kernel.stopGBean(targetRepositoryAName);
321: kernel.setAttribute(targetRepositoryAName, "root",
322: targetRepository.toURI());
323: kernel.startGBean(targetRepositoryAName);
324:
325: if (kernel.getGBeanState(targetConfigStoreAName) != State.RUNNING_INDEX) {
326: throw new IllegalStateException(
327: "After restarted repository then config store is not running");
328: }
329:
330: targetSet = true;
331: }
332:
333: log.debug("Starting configurations..."
334: + Arrays.asList(deploymentConfigs));
335:
336: // start the Configuration we're going to use for this deployment
337: ConfigurationManager configurationManager = ConfigurationUtil
338: .getConfigurationManager(kernel);
339: try {
340: for (String artifactName : deploymentConfigs) {
341: org.apache.geronimo.kernel.repository.Artifact configName = org.apache.geronimo.kernel.repository.Artifact
342: .create(artifactName);
343: if (!configurationManager.isLoaded(configName)) {
344: RecordingLifecycleMonitor monitor = new RecordingLifecycleMonitor();
345: try {
346: configurationManager.loadConfiguration(
347: configName, monitor);
348: } catch (LifecycleException e) {
349: log.error(
350: "Could not load deployer configuration: "
351: + configName + "\n"
352: + monitor.toString(), e);
353: }
354: monitor = new RecordingLifecycleMonitor();
355: try {
356: configurationManager.startConfiguration(
357: configName, monitor);
358: log.info("Started deployer: " + configName);
359: } catch (LifecycleException e) {
360: log.error(
361: "Could not start deployer configuration: "
362: + configName + "\n"
363: + monitor.toString(), e);
364: }
365: }
366: }
367: } finally {
368: ConfigurationUtil.releaseConfigurationManager(kernel,
369: configurationManager);
370: }
371:
372: log.debug("Deploying...");
373:
374: AbstractName deployer = locateDeployer(kernel);
375: invokeDeployer(kernel, deployer, targetConfigStoreAName
376: .toString());
377: //use a fresh kernel for each module
378: kernel.shutdown();
379: kernel = null;
380: }
381:
382: /**
383: * Create a Geronimo Kernel to contain the deployment configurations.
384: */
385: private synchronized Kernel createKernel() throws Exception {
386: // first return our cached version
387: if (kernel != null) {
388: return kernel;
389: }
390:
391: log.debug("Creating kernel...");
392:
393: // check the registry in case someone else created one
394: kernel = KernelRegistry.getKernel(KERNEL_NAME);
395: if (kernel != null) {
396: return kernel;
397: }
398:
399: GeronimoLogging geronimoLogging = GeronimoLogging
400: .getGeronimoLogging("WARN");
401: if (geronimoLogging == null) {
402: geronimoLogging = GeronimoLogging.DEBUG;
403: }
404: GeronimoLogging.initialize(geronimoLogging);
405:
406: // boot one ourselves
407: kernel = KernelFactory.newInstance().createKernel(KERNEL_NAME);
408: kernel.boot();
409:
410: bootDeployerSystem();
411:
412: return kernel;
413: }
414:
415: /**
416: * Boot the in-Maven deployment system.
417: *
418: * <p>
419: * This contains Repository and ConfigurationStore GBeans that map to
420: * the local maven installation.
421: * </p>
422: */
423: private void bootDeployerSystem() throws Exception {
424: log.debug("Booting deployer system...");
425:
426: org.apache.geronimo.kernel.repository.Artifact baseId = new org.apache.geronimo.kernel.repository.Artifact(
427: "geronimo", "packaging", "fixed", "car");
428: Naming naming = kernel.getNaming();
429: ConfigurationData bootstrap = new ConfigurationData(baseId,
430: naming);
431: ClassLoader cl = getClass().getClassLoader();
432: Set<AbstractName> repoNames = new HashSet<AbstractName>();
433:
434: //
435: // NOTE: Install an adapter for the source repository that will leverage the Maven2 repository subsystem
436: // to allow for better handling of SNAPSHOT values.
437: //
438: GBeanData repoGBean = bootstrap.addGBean("SourceRepository",
439: GBeanInfo.getGBeanInfo(Maven2RepositoryAdapter.class
440: .getName(), cl));
441: Maven2RepositoryAdapter.ArtifactLookup lookup = new Maven2RepositoryAdapter.ArtifactLookup() {
442: private Maven2RepositoryAdapter.ArtifactLookup getDelegate() {
443: return lookupHolder.get();
444: }
445:
446: public File getBasedir() {
447: return getDelegate().getBasedir();
448: }
449:
450: public File getLocation(
451: final org.apache.geronimo.kernel.repository.Artifact artifact) {
452: return getDelegate().getLocation(artifact);
453: }
454: };
455: repoGBean.setAttribute("lookup", lookup);
456: repoGBean.setAttribute("dependencies", dependencies);
457: repoNames.add(repoGBean.getAbstractName());
458:
459: // Target repo
460: GBeanData targetRepoGBean = bootstrap.addGBean(
461: "TargetRepository", GBeanInfo.getGBeanInfo(
462: Maven2Repository.class.getName(), cl));
463: URI targetRepositoryURI = targetRepository.toURI();
464: targetRepoGBean.setAttribute("root", targetRepositoryURI);
465: repoNames.add(targetRepoGBean.getAbstractName());
466: targetRepositoryAName = targetRepoGBean.getAbstractName();
467:
468: GBeanData artifactManagerGBean = bootstrap.addGBean(
469: "ArtifactManager", DefaultArtifactManager.GBEAN_INFO);
470: GBeanData artifactResolverGBean = bootstrap.addGBean(
471: "ArtifactResolver",
472: ExplicitDefaultArtifactResolver.GBEAN_INFO);
473: artifactResolverGBean.setAttribute("versionMapLocation",
474: explicitResolutionProperties.getAbsolutePath());
475: ReferencePatterns repoPatterns = new ReferencePatterns(
476: repoNames);
477: artifactResolverGBean.setReferencePatterns("Repositories",
478: repoPatterns);
479: artifactResolverGBean.setReferencePattern("ArtifactManager",
480: artifactManagerGBean.getAbstractName());
481:
482: Set storeNames = new HashSet();
483:
484: // Source config store
485: GBeanInfo configStoreInfo = GBeanInfo.getGBeanInfo(
486: MavenConfigStore.class.getName(), cl);
487: GBeanData storeGBean = bootstrap.addGBean("ConfigStore",
488: configStoreInfo);
489: if (configStoreInfo.getReference("Repository") != null) {
490: storeGBean.setReferencePattern("Repository", repoGBean
491: .getAbstractName());
492: }
493: storeNames.add(storeGBean.getAbstractName());
494:
495: // Target config store
496: GBeanInfo targetConfigStoreInfo = GBeanInfo.getGBeanInfo(
497: RepositoryConfigurationStore.class.getName(), cl);
498: GBeanData targetStoreGBean = bootstrap.addGBean(
499: "TargetConfigStore", targetConfigStoreInfo);
500: if (targetConfigStoreInfo.getReference("Repository") != null) {
501: targetStoreGBean.setReferencePattern("Repository",
502: targetRepoGBean.getAbstractName());
503: }
504: storeNames.add(targetStoreGBean.getAbstractName());
505:
506: targetConfigStoreAName = targetStoreGBean.getAbstractName();
507: targetSet = true;
508:
509: GBeanData attrManagerGBean = bootstrap.addGBean(
510: "AttributeStore", MavenAttributeStore.GBEAN_INFO);
511: GBeanData configManagerGBean = bootstrap.addGBean(
512: "ConfigManager", KernelConfigurationManager.GBEAN_INFO);
513: configManagerGBean.setReferencePatterns("Stores",
514: new ReferencePatterns(storeNames));
515: configManagerGBean.setReferencePattern("AttributeStore",
516: attrManagerGBean.getAbstractName());
517: configManagerGBean.setReferencePattern("ArtifactManager",
518: artifactManagerGBean.getAbstractName());
519: configManagerGBean.setReferencePattern("ArtifactResolver",
520: artifactResolverGBean.getAbstractName());
521: configManagerGBean.setReferencePatterns("Repositories",
522: repoPatterns);
523:
524: ConfigurationUtil.loadBootstrapConfiguration(kernel, bootstrap,
525: cl);
526: }
527:
528: /**
529: * Locate a Deployer GBean matching the deployerName pattern.
530: *
531: * @param kernel the kernel to search.
532: * @return the ObjectName of the Deployer GBean
533: *
534: * @throws IllegalStateException if there is not exactly one GBean matching the deployerName pattern
535: */
536: private AbstractName locateDeployer(final Kernel kernel) {
537: AbstractName name = new AbstractName(URI.create(deployerName));
538:
539: Iterator i = kernel.listGBeans(new AbstractNameQuery(name))
540: .iterator();
541: if (!i.hasNext()) {
542: throw new IllegalStateException(
543: "No deployer found matching deployerName: " + name);
544: }
545:
546: AbstractName deployer = (AbstractName) i.next();
547: if (i.hasNext()) {
548: throw new IllegalStateException(
549: "Multiple deployers found matching deployerName: "
550: + name);
551: }
552:
553: return deployer;
554: }
555:
556: private static final String[] DEPLOY_SIGNATURE = {
557: boolean.class.getName(), File.class.getName(),
558: File.class.getName(), File.class.getName(),
559: Boolean.TYPE.getName(), String.class.getName(),
560: String.class.getName(), String.class.getName(),
561: String.class.getName(), String.class.getName(),
562: String.class.getName(), String.class.getName(),
563: String.class.getName(), };
564:
565: private List invokeDeployer(final Kernel kernel,
566: final AbstractName deployer, final String targetConfigStore)
567: throws Exception {
568: Object[] args = { Boolean.FALSE, // Not in-place
569: moduleFile, planFile, null, // Target file
570: Boolean.TRUE, // Install
571: null, // main-class
572: null, // main-gbean
573: null, // main-method
574: null, // Manifest configurations
575: null, // class-path
576: null, // endorsed-dirs
577: null, // extension-dirs
578: targetConfigStore };
579:
580: return (List) kernel.invoke(deployer, "deploy", args,
581: DEPLOY_SIGNATURE);
582: }
583:
584: }
|