blob: 3ea7f5bc26c80a938cff113d88df49504e108e2e [file] [log] [blame]
package org.apache.aries.plugin.eba;
/*
* 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.
*/
import org.apache.maven.archiver.PomPropertiesUtil;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.archiver.zip.ZipArchiver;
import org.codehaus.plexus.util.DirectoryScanner;
import org.codehaus.plexus.util.FileUtils;
import org.apache.maven.shared.osgi.DefaultMaven2OsgiConverter;
import org.apache.maven.shared.osgi.Maven2OsgiConverter;
import aQute.lib.osgi.Analyzer;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
/**
* Builds Aries Enterprise Bundle Archive (eba) files.
*
* @version $Id: $
* @goal eba
* @phase package
* @requiresDependencyResolution test
*/
public class EbaMojo
extends AbstractMojo
{
public static final String APPLICATION_MF_URI = "META-INF/APPLICATION.MF";
private static final String[] DEFAULT_INCLUDES = {"**/**"};
/**
* Application manifest headers
*/
private static final String MANIFEST_VERSION = "Manifest-Version";
private static final String APPLICATION_MANIFESTVERSION = "Application-ManifestVersion";
private static final String APPLICATION_SYMBOLICNAME = "Application-SymbolicName";
private static final String APPLICATION_VERSION = "Application-Version";
private static final String APPLICATION_NAME = "Application-Name";
private static final String APPLICATION_DESCRIPTION = "Application-Description";
private static final String APPLICATION_CONTENT = "Application-Content";
private static final String APPLICATION_EXPORTSERVICE = "Application-ExportService";
private static final String APPLICATION_IMPORTSERVICE = "Application-ImportService";
private static final String APPLICATION_USEBUNDLE = "Use-Bundle";
/**
* Coverter for maven pom values to OSGi manifest values (pulled in from the maven-bundle-plugin)
*/
private Maven2OsgiConverter maven2OsgiConverter = new DefaultMaven2OsgiConverter();
/**
* Single directory for extra files to include in the eba.
*
* @parameter expression="${basedir}/src/main/eba"
* @required
*/
private File ebaSourceDirectory;
/**
* The location of the APPLICATION.MF file to be used within the eba file.
*
* @parameter expression="${basedir}/src/main/eba/META-INF/APPLICATION.MF"
*/
private File applicationManifestFile;
/**
* Specify if the generated jar file of this project should be
* included in the eba file ; default is true.
*
* @parameter
*/
private Boolean includeJar = Boolean.TRUE;
/**
* The location of the manifest file to be used within the eba file.
*
* @parameter expression="${basedir}/src/main/eba/META-INF/MANIFEST.MF"
*/
private File manifestFile;
/**
* Directory that resources are copied to during the build.
*
* @parameter expression="${project.build.directory}/${project.build.finalName}"
* @required
*/
private String workDirectory;
/**
* Directory that remote-resources puts legal files.
*
* @parameter expression="${project.build.directory}/maven-shared-archive-resources"
* @required
*/
private String sharedResources;
/**
* The directory for the generated eba.
*
* @parameter expression="${project.build.directory}"
* @required
*/
private String outputDirectory;
/**
* The name of the eba file to generate.
*
* @parameter alias="ebaName" expression="${project.build.finalName}"
* @required
*/
private String finalName;
/**
* The maven project.
*
* @parameter expression="${project}"
* @required
* @readonly
*/
private MavenProject project;
/**
* The Jar archiver.
*
* @component role="org.codehaus.plexus.archiver.Archiver" roleHint="zip"
* @required
*/
private ZipArchiver zipArchiver;
/**
* Whether to generate a manifest based on maven configuration.
*
* @parameter expression="${generateManifest}" default-value="false"
*/
private boolean generateManifest;
/**
* Configuration for the plugin.
*
* @parameter
*/
private Map instructions = new LinkedHashMap();;
/**
* Adding pom.xml and pom.properties to the archive.
*
* @parameter expression="${addMavenDescriptor}" default-value="true"
*/
private boolean addMavenDescriptor;
/**
* Include or not empty directories
*
* @parameter expression="${includeEmptyDirs}" default-value="true"
*/
private boolean includeEmptyDirs;
/**
* Whether creating the archive should be forced.
*
* @parameter expression="${forceCreation}" default-value="false"
*/
private boolean forceCreation;
/**
* Whether to follow transitive dependencies or use explicit dependencies.
*
* @parameter expression="${useTransitiveDependencies}" default-value="false"
*/
private boolean useTransitiveDependencies;
/**
* Define which bundles to include in the archive.
* none - no bundles are included
* applicationContent - direct dependencies go into the content
* all - direct and transitive dependencies go into the content
*
* @parameter expression="${archiveContent}" default-value="applicationContent"
*/
private String archiveContent;
private File buildDir;
public void execute()
throws MojoExecutionException
{
getLog().debug( " ======= EbaMojo settings =======" );
getLog().debug( "ebaSourceDirectory[" + ebaSourceDirectory + "]" );
getLog().debug( "manifestFile[" + manifestFile + "]" );
getLog().debug( "applicationManifestFile[" + applicationManifestFile + "]" );
getLog().debug( "workDirectory[" + workDirectory + "]" );
getLog().debug( "outputDirectory[" + outputDirectory + "]" );
getLog().debug( "finalName[" + finalName + "]" );
getLog().debug( "generateManifest[" + generateManifest + "]" );
if (archiveContent == null) {
archiveContent = new String("applicationContent");
}
getLog().debug( "archiveContent[" + archiveContent + "]" );
getLog().info( "archiveContent[" + archiveContent + "]" );
zipArchiver.setIncludeEmptyDirs( includeEmptyDirs );
zipArchiver.setCompress( true );
zipArchiver.setForced( forceCreation );
// Check if jar file is there and if requested, copy it
try
{
if (includeJar.booleanValue()) {
File generatedJarFile = new File( outputDirectory, finalName + ".jar" );
if (generatedJarFile.exists()) {
getLog().info( "Including generated jar file["+generatedJarFile.getName()+"]");
zipArchiver.addFile(generatedJarFile, finalName + ".jar");
}
}
}
catch ( ArchiverException e )
{
throw new MojoExecutionException( "Error adding generated Jar file", e );
}
// Copy dependencies
try
{
Set<Artifact> artifacts = null;
if (useTransitiveDependencies || "all".equals(archiveContent)) {
// if use transitive is set (i.e. true) then we need to make sure archiveContent does not contradict (i.e. is set
// to the same compatible value or is the default).
if ("none".equals(archiveContent)) {
throw new MojoExecutionException("<useTransitiveDependencies/> and <archiveContent/> incompatibly configured. <useTransitiveDependencies/> is deprecated in favor of <archiveContent/>." );
}
else {
artifacts = project.getArtifacts();
}
} else {
// check that archiveContent is compatible
if ("applicationContent".equals(archiveContent)) {
artifacts = project.getDependencyArtifacts();
}
else {
// the only remaining options should be applicationContent="none"
getLog().info("archiveContent=none: application arvhive will not contain any bundles.");
}
}
if (artifacts != null) {
for (Artifact artifact : artifacts) {
ScopeArtifactFilter filter = new ScopeArtifactFilter(Artifact.SCOPE_RUNTIME);
if (!artifact.isOptional() && filter.include(artifact)) {
getLog().info("Copying artifact[" + artifact.getGroupId() + ", " + artifact.getId() + ", " +
artifact.getScope() + "]");
zipArchiver.addFile(artifact.getFile(), artifact.getArtifactId() + "-" + artifact.getVersion() + "." + (artifact.getType() == null ? "jar" : artifact.getType()));
}
}
}
}
catch ( ArchiverException e )
{
throw new MojoExecutionException( "Error copying EBA dependencies", e );
}
// Copy source files
try
{
File ebaSourceDir = ebaSourceDirectory;
if ( ebaSourceDir.exists() )
{
getLog().info( "Copy eba resources to " + getBuildDir().getAbsolutePath() );
DirectoryScanner scanner = new DirectoryScanner();
scanner.setBasedir( ebaSourceDir.getAbsolutePath() );
scanner.setIncludes( DEFAULT_INCLUDES );
scanner.addDefaultExcludes();
scanner.scan();
String[] dirs = scanner.getIncludedDirectories();
for ( int j = 0; j < dirs.length; j++ )
{
new File( getBuildDir(), dirs[j] ).mkdirs();
}
String[] files = scanner.getIncludedFiles();
for ( int j = 0; j < files.length; j++ )
{
File targetFile = new File( getBuildDir(), files[j] );
targetFile.getParentFile().mkdirs();
File file = new File( ebaSourceDir, files[j] );
FileUtils.copyFileToDirectory( file, targetFile.getParentFile() );
}
}
}
catch ( Exception e )
{
throw new MojoExecutionException( "Error copying EBA resources", e );
}
// Include custom manifest if necessary
try
{
if (!generateManifest) {
includeCustomApplicationManifestFile();
}
}
catch ( IOException e )
{
throw new MojoExecutionException( "Error copying APPLICATION.MF file", e );
}
// Generate application manifest if requested
if (generateManifest) {
String fileName = new String(getBuildDir() + "/"
+ APPLICATION_MF_URI);
File appMfFile = new File(fileName);
try {
// Delete any old manifest
if (appMfFile.exists()) {
FileUtils.fileDelete(fileName);
}
appMfFile.getParentFile().mkdirs();
if (appMfFile.createNewFile()) {
writeApplicationManifest(fileName);
}
} catch (java.io.IOException e) {
throw new MojoExecutionException(
"Error generating APPLICATION.MF file: " + fileName, e);
}
}
// Check if connector deployment descriptor is there
File ddFile = new File( getBuildDir(), APPLICATION_MF_URI);
if ( !ddFile.exists() )
{
getLog().warn(
"Application manifest: " + ddFile.getAbsolutePath() + " does not exist." );
}
try
{
if (addMavenDescriptor) {
if (project.getArtifact().isSnapshot()) {
project.setVersion(project.getArtifact().getVersion());
}
String groupId = project.getGroupId();
String artifactId = project.getArtifactId();
zipArchiver.addFile(project.getFile(), "META-INF/maven/" + groupId + "/" + artifactId + "/pom.xml");
PomPropertiesUtil pomPropertiesUtil = new PomPropertiesUtil();
File dir = new File(project.getBuild().getDirectory(), "maven-zip-plugin");
File pomPropertiesFile = new File(dir, "pom.properties");
pomPropertiesUtil.createPomProperties(project, zipArchiver, pomPropertiesFile, forceCreation);
}
File ebaFile = new File( outputDirectory, finalName + ".eba" );
zipArchiver.setDestFile(ebaFile);
File buildDir = getBuildDir();
if (buildDir.isDirectory()) {
zipArchiver.addDirectory(buildDir);
}
//include legal files if any
File sharedResourcesDir = new File(sharedResources);
if (sharedResourcesDir.isDirectory()) {
zipArchiver.addDirectory(sharedResourcesDir);
}
zipArchiver.createArchive();
project.getArtifact().setFile( ebaFile );
}
catch ( Exception e )
{
throw new MojoExecutionException( "Error assembling eba", e );
}
}
private void writeApplicationManifest(String fileName)
throws MojoExecutionException {
try {
// TODO: add support for dependency version ranges. Need to pick
// them up from the pom and convert them to OSGi version ranges.
FileUtils.fileAppend(fileName, MANIFEST_VERSION + ": " + "1" + "\n");
FileUtils.fileAppend(fileName, APPLICATION_MANIFESTVERSION + ": " + "1" + "\n");
FileUtils.fileAppend(fileName, APPLICATION_SYMBOLICNAME + ": "
+ getApplicationSymbolicName(project.getArtifact()) + "\n");
FileUtils.fileAppend(fileName, APPLICATION_VERSION + ": "
+ getApplicationVersion() + "\n");
FileUtils.fileAppend(fileName, APPLICATION_NAME + ": " + project.getName() + "\n");
FileUtils.fileAppend(fileName, APPLICATION_DESCRIPTION + ": "
+ project.getDescription() + "\n");
// Write the APPLICATION-CONTENT
// TODO: check that the dependencies are bundles (currently, the converter
// will throw an exception)
Set<Artifact> artifacts;
if (useTransitiveDependencies) {
artifacts = project.getArtifacts();
} else {
artifacts = project.getDependencyArtifacts();
}
artifacts = selectArtifacts(artifacts);
Iterator<Artifact> iter = artifacts.iterator();
FileUtils.fileAppend(fileName, APPLICATION_CONTENT + ": ");
if (iter.hasNext()) {
Artifact artifact = iter.next();
FileUtils.fileAppend(fileName, maven2OsgiConverter
.getBundleSymbolicName(artifact)
+ ";version=\""
+ Analyzer.cleanupVersion(artifact.getVersion())
// + maven2OsgiConverter.getVersion(artifact.getVersion())
+ "\"");
}
while (iter.hasNext()) {
Artifact artifact = iter.next();
FileUtils.fileAppend(fileName, ",\n "
+ maven2OsgiConverter.getBundleSymbolicName(artifact)
+ ";version=\""
+ Analyzer.cleanupVersion(artifact.getVersion())
// + maven2OsgiConverter.getVersion(artifact.getVersion())
+ "\"");
}
FileUtils.fileAppend(fileName, "\n");
// Add any service imports or exports
if (instructions.containsKey(APPLICATION_EXPORTSERVICE)) {
FileUtils.fileAppend(fileName, APPLICATION_EXPORTSERVICE + ": "
+ instructions.get(APPLICATION_EXPORTSERVICE) + "\n");
}
if (instructions.containsKey(APPLICATION_IMPORTSERVICE)) {
FileUtils.fileAppend(fileName, APPLICATION_IMPORTSERVICE + ": "
+ instructions.get(APPLICATION_IMPORTSERVICE) + "\n");
}
if (instructions.containsKey(APPLICATION_USEBUNDLE)) {
FileUtils.fileAppend(fileName, APPLICATION_USEBUNDLE + ": "
+ instructions.get(APPLICATION_USEBUNDLE) + "\n");
}
// Add any use bundle entry
} catch (Exception e) {
throw new MojoExecutionException(
"Error writing dependencies into APPLICATION.MF", e);
}
}
// The maven2OsgiConverter assumes the artifact is a jar so we need our own
// This uses the same fallback scheme as the converter
private String getApplicationSymbolicName(Artifact artifact) {
if (instructions.containsKey(APPLICATION_SYMBOLICNAME)) {
return instructions.get(APPLICATION_SYMBOLICNAME).toString();
}
return artifact.getGroupId() + "." + artifact.getArtifactId();
}
private String getApplicationVersion() {
if (instructions.containsKey(APPLICATION_VERSION)) {
return instructions.get(APPLICATION_VERSION).toString();
}
return aQute.lib.osgi.Analyzer.cleanupVersion(project.getVersion());
}
protected File getBuildDir()
{
if ( buildDir == null )
{
buildDir = new File( workDirectory );
}
return buildDir;
}
private void includeCustomApplicationManifestFile()
throws IOException
{
if (applicationManifestFile == null) {
throw new NullPointerException("Application manifest file location not set. Use <generateManifest>true</generateManifest> if you want it to be generated.");
}
File appMfFile = applicationManifestFile;
if (appMfFile.exists()) {
getLog().info( "Using APPLICATION.MF "+ applicationManifestFile);
File metaInfDir = new File(getBuildDir(), "META-INF");
FileUtils.copyFileToDirectory( appMfFile, metaInfDir);
}
}
/**
* Return artifacts in 'compile' or 'runtime' scope only.
*/
private Set<Artifact> selectArtifacts(Set<Artifact> artifacts)
{
Set<Artifact> selected = new LinkedHashSet<Artifact>();
for (Artifact artifact : artifacts) {
String scope = artifact.getScope();
if (scope == null
|| Artifact.SCOPE_COMPILE.equals(scope)
|| Artifact.SCOPE_RUNTIME.equals(scope)) {
selected.add(artifact);
}
}
return selected;
}
}