| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.apache.synapse.maven.xar; |
| |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStreamWriter; |
| import java.io.Writer; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.apache.maven.artifact.Artifact; |
| import org.apache.maven.artifact.factory.ArtifactFactory; |
| import org.apache.maven.artifact.metadata.ArtifactMetadataSource; |
| import org.apache.maven.artifact.repository.ArtifactRepository; |
| import org.apache.maven.artifact.resolver.ArtifactCollector; |
| import org.apache.maven.artifact.resolver.ArtifactResolutionException; |
| import org.apache.maven.artifact.resolver.DebugResolutionListener; |
| import org.apache.maven.artifact.resolver.filter.AndArtifactFilter; |
| import org.apache.maven.artifact.resolver.filter.ArtifactFilter; |
| import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter; |
| import org.apache.maven.artifact.resolver.filter.TypeArtifactFilter; |
| import org.apache.maven.plugin.AbstractMojo; |
| import org.apache.maven.plugin.MojoExecutionException; |
| import org.apache.maven.plugin.logging.Log; |
| import org.apache.maven.project.MavenProject; |
| import org.apache.maven.project.MavenProjectBuilder; |
| import org.apache.maven.project.ProjectBuildingException; |
| import org.apache.maven.project.artifact.InvalidDependencyVersionException; |
| import org.codehaus.plexus.archiver.Archiver; |
| import org.codehaus.plexus.archiver.ArchiverException; |
| import org.codehaus.plexus.logging.LogEnabled; |
| import org.codehaus.plexus.logging.Logger; |
| |
| /** |
| * Abstract base class for all the mojos in the XAR plugin. |
| */ |
| public abstract class AbstractXarMojo extends AbstractMojo implements LogEnabled { |
| /** |
| * List of dependencies to be excluded by default because the corresponding APIs are provided |
| * by the Synapse runtime. |
| */ |
| private static final String[] defaultRuntimeExcludes = { |
| "org.apache.synapse:synapse-core:jar", |
| "commons-logging:commons-logging-api:jar", |
| }; |
| |
| private static final String[] serviceClassNames = { |
| "org.apache.synapse.config.xml.MediatorFactory", |
| "org.apache.synapse.config.xml.MediatorSerializer", |
| "org.apache.synapse.config.xml.StartupFactory", |
| }; |
| |
| /** |
| * The projects base directory. |
| * |
| * @parameter expression="${project.basedir}" |
| * @required |
| * @readonly |
| */ |
| private File baseDir; |
| |
| /** |
| * The maven project. |
| * |
| * @parameter expression="${project}" |
| * @required |
| * @readonly |
| */ |
| protected MavenProject project; |
| |
| /** |
| * Local maven repository. |
| * |
| * @parameter expression="${localRepository}" |
| * @required |
| * @readonly |
| */ |
| private ArtifactRepository localRepository; |
| |
| /** |
| * Remote repositories. |
| * |
| * @parameter expression="${project.remoteArtifactRepositories}" |
| * @required |
| * @readonly |
| */ |
| private List remoteArtifactRepositories; |
| |
| /** |
| * @component role="org.apache.maven.artifact.metadata.ArtifactMetadataSource" hint="maven" |
| */ |
| private ArtifactMetadataSource artifactMetadataSource; |
| |
| /** |
| * Artifact collector, needed to resolve dependencies. |
| * |
| * @component role="org.apache.maven.artifact.resolver.ArtifactCollector" |
| * @required |
| * @readonly |
| */ |
| private ArtifactCollector artifactCollector; |
| |
| /** |
| * Project builder. |
| * |
| * @component role="org.apache.maven.project.MavenProjectBuilder" |
| * @required |
| * @readonly |
| */ |
| private MavenProjectBuilder projectBuilder; |
| |
| /** |
| * Artifact factory. |
| * |
| * @component role="org.apache.maven.artifact.factory.ArtifactFactory" |
| * @required |
| * @readonly |
| */ |
| private ArtifactFactory artifactFactory; |
| |
| /** |
| * The directory containing generated classes. |
| * |
| * @parameter expression="${project.build.outputDirectory}" |
| * @required |
| */ |
| private File buildOutputDirectory; |
| |
| /** |
| * The directory where temporary files for inclusion in the XAR are stored. |
| * |
| * @parameter expression="${project.build.directory}/xar-files" |
| * @required |
| */ |
| private File tmpDirectory; |
| |
| /** |
| * Whether the dependency jars should be included in the XAR. |
| * |
| * @parameter expression="${includeDependencies}" default-value="true" |
| */ |
| private boolean includeDependencies; |
| |
| /** |
| * Whether metadata for the extensions should be generated automatically. |
| * |
| * @parameter expression="${generateMetadata}" default-value="true" |
| */ |
| private boolean generateMetadata; |
| |
| private Logger logger; |
| |
| public void enableLogging(Logger logger) { |
| this.logger = logger; |
| } |
| |
| /** |
| * Build the XAR using the provided archiver. |
| * |
| * @throws MojoExecutionException |
| */ |
| protected void buildArchive(Archiver archiver) |
| throws ArchiverException, MojoExecutionException { |
| |
| Log log = getLog(); |
| log.debug("Using base directory: " + baseDir); |
| archiver.addDirectory(buildOutputDirectory); |
| if (includeDependencies) { |
| log.debug("Adding dependencies ..."); |
| addDependencies(archiver); |
| } |
| if (generateMetadata) { |
| log.debug("Generating XAR metadata ..."); |
| generateMetadata(archiver); |
| } |
| } |
| |
| private void addDependencies(Archiver archiver) |
| throws ArchiverException, MojoExecutionException { |
| |
| Log log = getLog(); |
| AndArtifactFilter filter = new AndArtifactFilter(); |
| filter.add(new ScopeArtifactFilter(Artifact.SCOPE_RUNTIME)); |
| filter.add(new ArtifactFilter() { |
| public boolean include(Artifact artifact) { |
| return !artifact.isOptional(); |
| } |
| }); |
| filter.add(new TypeArtifactFilter("jar")); |
| filter.add(buildSynapseRuntimeArtifactFilter()); |
| for (Artifact artifact : filterArtifacts(project.getArtifacts(), filter)) { |
| String targetFileName = artifact.getArtifactId() + "-" + artifact.getVersion() + "." + |
| artifact.getArtifactHandler().getExtension(); |
| log.info("Adding " + targetFileName + " (scope " + artifact.getScope() + ")"); |
| archiver.addFile(artifact.getFile(), "lib/" + targetFileName); |
| } |
| } |
| |
| private void generateMetadata(Archiver archiver) |
| throws ArchiverException, MojoExecutionException { |
| |
| Log log = getLog(); |
| File tmpServicesDir = new File(new File(tmpDirectory, "META-INF"), "services"); |
| File buildServicesDir = new File(new File(buildOutputDirectory, "META-INF"), "services"); |
| if (!tmpServicesDir.exists() && !tmpServicesDir.mkdirs()) { |
| throw new MojoExecutionException("Error while creating the directory: " + |
| tmpServicesDir.getPath()); |
| } |
| |
| log.debug("Initializing class scanner ..."); |
| ClassScanner scanner = new ClassScanner(buildOutputDirectory); |
| for (Artifact artifact : filterArtifacts(project.getArtifacts(), |
| new ScopeArtifactFilter(Artifact.SCOPE_COMPILE))) { |
| scanner.addToClasspath(artifact.getFile()); |
| } |
| List<ServiceLocator> serviceLocators = |
| new ArrayList<ServiceLocator>(serviceClassNames.length); |
| for (String serviceClassName : serviceClassNames) { |
| // If the user provided its own service file, skip generation |
| File file = new File(buildServicesDir, serviceClassName); |
| if (file.exists()) { |
| log.debug(file + " exists; don't scan for " + serviceClassName + |
| " implementation"); |
| } else { |
| ServiceLocator sl = new ServiceLocator(serviceClassName); |
| serviceLocators.add(sl); |
| scanner.addVisitor(sl); |
| } |
| } |
| try { |
| scanner.scan(); |
| } catch (ClassScannerException e) { |
| throw new MojoExecutionException("Failed to scan classes for services", e); |
| } |
| for (ServiceLocator sl : serviceLocators) { |
| File file = new File(tmpServicesDir, sl.getServiceClassName()); |
| if (!sl.getImplementations().isEmpty()) { |
| String destFileName = "META-INF/services/" + sl.getServiceClassName(); |
| log.info("Generating " + destFileName); |
| try { |
| Writer out = new OutputStreamWriter(new FileOutputStream(file)); |
| try { |
| for (String impl : sl.getImplementations()) { |
| log.debug(" " + impl); |
| out.write(impl); |
| out.write("\n"); |
| } |
| } finally { |
| out.close(); |
| } |
| } catch (IOException e) { |
| throw new MojoExecutionException("Unable to create temporary file " + file, e); |
| } |
| archiver.addFile(file, destFileName); |
| } |
| } |
| } |
| |
| /** |
| * Build a filter that excludes all artifacts that are provided by Synapse at runtime. |
| * |
| * @return |
| * @throws MojoExecutionException |
| */ |
| private ArtifactFilter buildSynapseRuntimeArtifactFilter() throws MojoExecutionException { |
| final Map<String,Artifact> artifacts = new HashMap<String,Artifact>(); |
| for (Artifact artifact : getSynapseRuntimeArtifacts()) { |
| artifacts.put(artifact.getDependencyConflictId(), artifact); |
| } |
| final Set<String> defaultExclusionSet |
| = new HashSet<String>(Arrays.asList(defaultRuntimeExcludes)); |
| return new ArtifactFilter() { |
| public boolean include(Artifact artifact) { |
| Artifact runtimeArtifact = artifacts.get(artifact.getDependencyConflictId()); |
| if (runtimeArtifact == null) { |
| return !defaultExclusionSet.contains(artifact.getDependencyConflictId()); |
| } else { |
| if (!runtimeArtifact.getVersion().equals(artifact.getVersion())) { |
| getLog().warn("Possible runtime version conflict for " |
| + artifact.getArtifactId() + ": XAR depends on " |
| + artifact.getVersion() + ", Synapse runtime provides " |
| + runtimeArtifact.getVersion()); |
| } |
| return false; |
| } |
| } |
| }; |
| } |
| |
| /** |
| * Get the set of artifacts that are provided by Synapse at runtime. |
| * |
| * @return |
| * @throws MojoExecutionException |
| */ |
| private Set<Artifact> getSynapseRuntimeArtifacts() throws MojoExecutionException { |
| Log log = getLog(); |
| log.debug("Looking for synapse-core artifact in XAR project dependencies ..."); |
| Artifact synapseCore = null; |
| for (Iterator<?> it = project.getDependencyArtifacts().iterator(); it.hasNext(); ) { |
| Artifact artifact = (Artifact)it.next(); |
| if (artifact.getGroupId().equals("org.apache.synapse") |
| && artifact.getArtifactId().equals("synapse-core")) { |
| synapseCore = artifact; |
| break; |
| } |
| } |
| if (synapseCore == null) { |
| throw new MojoExecutionException("Could not locate dependency on synapse-core"); |
| } |
| |
| log.debug("Loading project data for " + synapseCore + " ..."); |
| MavenProject synapseCoreProject; |
| try { |
| synapseCoreProject = projectBuilder.buildFromRepository(synapseCore, |
| remoteArtifactRepositories, localRepository); |
| } catch (ProjectBuildingException e) { |
| throw new MojoExecutionException("Unable to retrieve project information for " |
| + synapseCore, e); |
| } |
| Set<Artifact> synapseRuntimeDeps; |
| try { |
| synapseRuntimeDeps = synapseCoreProject.createArtifacts(artifactFactory, |
| Artifact.SCOPE_RUNTIME, new TypeArtifactFilter("jar")); |
| } catch (InvalidDependencyVersionException e) { |
| throw new MojoExecutionException("Unable to get project dependencies for " |
| + synapseCore, e); |
| } |
| log.debug("Direct runtime dependencies for " + synapseCore + " :"); |
| logArtifacts(synapseRuntimeDeps); |
| |
| log.debug("Resolving transitive dependencies for " + synapseCore + " ..."); |
| try { |
| synapseRuntimeDeps = artifactCollector.collect(synapseRuntimeDeps, |
| synapseCoreProject.getArtifact(), synapseCoreProject.getManagedVersionMap(), |
| localRepository, remoteArtifactRepositories, artifactMetadataSource, null, |
| Collections.singletonList(new DebugResolutionListener(logger))).getArtifacts(); |
| } catch (ArtifactResolutionException e) { |
| throw new MojoExecutionException("Unable to resolve transitive dependencies for " |
| + synapseCore); |
| } |
| log.debug("All runtime dependencies for " + synapseCore + " :"); |
| logArtifacts(synapseRuntimeDeps); |
| |
| return synapseRuntimeDeps; |
| } |
| |
| private void logArtifacts(Collection<Artifact> collection) { |
| List<Artifact> artifacts = new ArrayList<Artifact>(collection); |
| Collections.sort(artifacts, new Comparator<Artifact>() { |
| public int compare(Artifact o1, Artifact o2) { |
| return o1.getArtifactId().compareTo(o2.getArtifactId()); |
| } |
| }); |
| for (Artifact artifact : artifacts) { |
| getLog().debug(" " + artifact.getArtifactId() + "-" + artifact.getVersion() |
| + "." + artifact.getArtifactHandler().getExtension()); |
| } |
| } |
| |
| private static Set<Artifact> filterArtifacts(Set<Artifact> artifacts, ArtifactFilter filter) { |
| Set<Artifact> result = new HashSet<Artifact>(); |
| for (Artifact artifact : artifacts) { |
| if (filter.include(artifact)) { |
| result.add(artifact); |
| } |
| } |
| return result; |
| } |
| } |