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.net.URI; 024 025 import java.util.ArrayList; 026 import java.util.List; 027 import java.util.Iterator; 028 import java.util.Set; 029 import java.util.HashSet; 030 031 import org.apache.geronimo.deployment.PluginBootstrap2; 032 import org.apache.geronimo.system.configuration.RepositoryConfigurationStore; 033 import org.apache.geronimo.system.repository.Maven2Repository; 034 import org.apache.geronimo.system.resolver.ExplicitDefaultArtifactResolver; 035 import org.apache.geronimo.kernel.Kernel; 036 import org.apache.geronimo.kernel.KernelRegistry; 037 import org.apache.geronimo.kernel.KernelFactory; 038 import org.apache.geronimo.kernel.Naming; 039 import org.apache.geronimo.kernel.repository.DefaultArtifactManager; 040 import org.apache.geronimo.kernel.log.GeronimoLogging; 041 import org.apache.geronimo.kernel.management.State; 042 import org.apache.geronimo.kernel.config.ConfigurationManager; 043 import org.apache.geronimo.kernel.config.ConfigurationUtil; 044 import org.apache.geronimo.kernel.config.ConfigurationData; 045 import org.apache.geronimo.kernel.config.KernelConfigurationManager; 046 import org.apache.geronimo.gbean.AbstractName; 047 import org.apache.geronimo.gbean.GBeanData; 048 import org.apache.geronimo.gbean.GBeanInfo; 049 import org.apache.geronimo.gbean.ReferencePatterns; 050 import org.apache.geronimo.gbean.AbstractNameQuery; 051 052 import org.apache.geronimo.genesis.ArtifactItem; 053 054 import org.apache.maven.archiver.MavenArchiveConfiguration; 055 import org.apache.maven.archiver.MavenArchiver; 056 import org.apache.maven.plugin.MojoExecutionException; 057 import org.apache.maven.artifact.Artifact; 058 059 import org.codehaus.plexus.util.FileUtils; 060 import org.codehaus.plexus.archiver.jar.JarArchiver; 061 062 /** 063 * Build a Geronimo Configuration using the local Maven infrastructure. 064 * 065 * <p> 066 * <b>NOTE:</b> Calling pom.xml must have defined a ${geronimoVersion} property. 067 * </p> 068 * 069 * @goal package 070 * @requiresDependencyResolution runtime 071 * 072 * @version $Rev: 451661 $ $Date: 2006-09-30 13:45:53 -0700 (Sat, 30 Sep 2006) $ 073 */ 074 public class PackageMojo 075 extends AbstractCarMojo 076 { 077 /** 078 * The maven archive configuration to use. 079 * 080 * See <a href="http://maven.apache.org/ref/current/maven-archiver/apidocs/org/apache/maven/archiver/MavenArchiveConfiguration.html">the Javadocs for MavenArchiveConfiguration</a>. 081 * 082 * @parameter 083 */ 084 private MavenArchiveConfiguration archive = new MavenArchiveConfiguration(); 085 086 /** 087 * The Jar archiver. 088 * 089 * @parameter expression="${component.org.codehaus.plexus.archiver.Archiver#jar}" 090 * @required 091 * @readonly 092 */ 093 private JarArchiver jarArchiver = null; 094 095 /** 096 * Directory containing the generated archive. 097 * 098 * @parameter expression="${project.build.directory}" 099 * @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 }