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 }