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