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