/* * 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.tuscany.maven.bundle.plugin; import static org.apache.tuscany.maven.bundle.plugin.BundleUtil.write; import static org.osgi.framework.Constants.BUNDLE_CLASSPATH; import static org.osgi.framework.Constants.BUNDLE_VERSION; import static org.osgi.framework.Constants.RESOLUTION_DIRECTIVE; import static org.osgi.framework.Constants.RESOLUTION_OPTIONAL; import static org.osgi.framework.Constants.VISIBILITY_DIRECTIVE; import static org.osgi.framework.Constants.VISIBILITY_REEXPORT; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.jar.Attributes; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import java.util.zip.ZipEntry; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.artifact.metadata.ArtifactMetadataSource; import org.apache.maven.artifact.resolver.ArtifactCollector; import org.apache.maven.artifact.resolver.ArtifactNotFoundException; import org.apache.maven.artifact.resolver.ArtifactResolutionException; import org.apache.maven.artifact.resolver.ArtifactResolutionResult; import org.apache.maven.artifact.resolver.filter.ArtifactFilter; import org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter; import org.apache.maven.artifact.versioning.ArtifactVersion; import org.apache.maven.artifact.versioning.DefaultArtifactVersion; 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.apache.maven.shared.dependency.tree.DependencyNode; import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder; import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException; import org.apache.maven.shared.dependency.tree.traversal.CollectingDependencyNodeVisitor; import org.osgi.framework.Constants; /** * A maven plugin that generates a modules directory containing OSGi bundles for all the project's module dependencies. * * @version $Rev$ $Date$ * @goal generate-modules * @phase generate-resources * @requiresDependencyResolution test * @description Generate a modules directory containing OSGi bundles for all the project's module dependencies. */ public class ModuleBundlesBuildMojo extends AbstractMojo { private static final String GATEWAY_BUNDLE = "org.apache.tuscany.sca.gateway"; /** * The project to create a distribution for. * * @parameter expression="${project}" * @required * @readonly */ private MavenProject project; /** * Project builder -- builds a model from a pom.xml * * @component role="org.apache.maven.project.MavenProjectBuilder" * @required * @readonly */ private MavenProjectBuilder mavenProjectBuilder; /** * Used to look up Artifacts in the remote repository. * * @component */ private org.apache.maven.artifact.factory.ArtifactFactory factory; /** * Used to look up Artifacts in the remote repository. * * @component */ private org.apache.maven.artifact.resolver.ArtifactResolver resolver; /** * @component role="org.apache.maven.artifact.metadata.ArtifactMetadataSource" * hint="maven" * @required * @readonly */ private ArtifactMetadataSource artifactMetadataSource; /** * The artifact collector to use. * * @component * @required * @readonly */ private ArtifactCollector artifactCollector; /** * The dependency tree builder to use. * * @component * @required * @readonly */ private DependencyTreeBuilder dependencyTreeBuilder; /** * Location of the local repository. * * @parameter expression="${localRepository}" * @readonly * @required */ private org.apache.maven.artifact.repository.ArtifactRepository local; /** * List of Remote Repositories used by the resolver * * @parameter expression="${project.remoteArtifactRepositories}" * @readonly * @required */ private java.util.List remoteRepos; /** * Target directory. * * @parameter expression="${project.build.directory}/modules" */ private File targetDirectory; /** * @parameter default-value="features" */ private String featuresName = "features"; /** * Directories containing artifacts to exclude. * * @parameter */ private File[] excludeDirectories; /** * Directories containing groupids to exclude. * * @parameter */ private String[] excludeGroupIds; /** * Directories containing groupids to include. * * @parameter */ private String[] includeGroupIds; /** * Set to true to generate configurations under a folder named as the distro * * @parameter default-value="true" */ private boolean useDistributionName = true; /** * Set to true to generate a PDE target platform configuration. * * @parameter default-value="true" */ private boolean generateTargetPlatform = true; /** * Expand non-tuscany bundles as a folder * @parameter default-value="false" */ private boolean expandThirdPartyBundle = false; /** * OSGi execution environment */ private String executionEnvironment; /** * A list of Eclipse features to be added to the target definition * @parameter */ private String[] eclipseFeatures; /** * If we use the running eclipse as the default location for the target * @parameter default-value="true" */ private boolean useDefaultLocation = true; /** * Set to true to generate a gateway bundle tuscany-gateway-.jar that handles split packages and META-INF/services. * * @parameter default-value="true" */ private boolean generateGatewayBundle; /** * @parameter default-value="false" */ private boolean gatewayReexport; /** * Set to true to generate a plugin.xml. * * @parameter default-value="false" */ private boolean generatePlugin; /** * Generate a configuration/config.ini for equinox * @parameter default-value="true" */ private boolean generateConfig = true; /** * Generate an aggregated OSGi bundle for each feature * @parameter default-value="false" */ private boolean generateAggregatedBundle = false; /** * @parameter default-value="true" */ private boolean generateBundleStart = true; /** * @parameter default-value="true" */ private boolean includeConflictingDepedencies = true; /** * Generete manifest.jar * @parameter default-value="true" */ private boolean generateManifestJar = true; /** * @parameter default-value="tuscany-sca-manifest.jar" */ private String manifestJarName = "tuscany-sca-manifest.jar"; /** * @parameter default-value="tuscany-sca-equinox-manifest.jar" */ private String equinoxManifestJarName = "tuscany-sca-equinox-manifest.jar"; /** * @parameter default-value="jar,bundle" */ private String artifactTypes; /** * @parameter default-value="true" */ private boolean generateAntScript = true; /** * @parameter */ private ArtifactAggregation[] artifactAggregations; /** * @parameter */ private ArtifactManifest[] artifactManifests; /** * Inserts a generic Eclipse-BuddyPolicy header into generated artifacts manifests * @parameter */ private String eclipseBuddyPolicy = null; private static final String XML_PI = ""; private static final String ASL_HEADER = ""; /** * Group the artifacts by distribution poms */ private class ProjectSet { // Distribution projects private Map projects; // Key: the pom artifact id // Value: the names for the artifacts private Map> nameMap = new HashMap>(); private Map artifactToNameMap = new HashMap(); public ProjectSet(List projects) { super(); this.projects = new HashMap(); for (MavenProject p : projects) { this.projects.put(p.getArtifactId(), p); } } private MavenProject getProject(String artifactId) { return projects.get(artifactId); } private void add(Artifact artifact, String name) { String key = ArtifactUtils.versionlessKey(artifact); for (MavenProject p : projects.values()) { Artifact a = (Artifact)p.getArtifactMap().get(key); if (a != null) { Set names = nameMap.get(p.getArtifactId()); if (names == null) { names = new HashSet(); nameMap.put(p.getArtifactId(), names); } names.add(name); } } artifactToNameMap.put(key, name); } } private Manifest findManifest(Artifact artifact) throws IOException { if (artifactManifests == null) { return null; } for (ArtifactManifest m : artifactManifests) { if (m.matches(artifact)) { File mf = m.getManifestFile(); if (mf != null) { FileInputStream is = new FileInputStream(mf); Manifest manifest = new Manifest(is); is.close(); getLog().info("MANIFEST.MF found for " + artifact + " (" + mf + ")"); return manifest; } else { getLog().info("Overriding the manifest for " + artifact); Manifest manifest = BundleUtil.getManifest(artifact.getFile()); Set jarFiles = new HashSet(); jarFiles.add(artifact.getFile()); String symbolicName = BundleUtil.getBundleSymbolicName(manifest); if (symbolicName == null) { // Not a bundle continue; } String version = manifest.getMainAttributes().getValue(BUNDLE_VERSION); manifest = BundleUtil.libraryManifest(jarFiles, symbolicName, symbolicName, version, null, this.eclipseBuddyPolicy, this.executionEnvironment); // Remove it as it will be added later on manifest.getMainAttributes().remove(new Attributes.Name(BUNDLE_CLASSPATH)); return manifest; } } } return null; } /** * Gets the artifact filter to use when resolving the dependency tree. * * @return the artifact filter */ private ArtifactFilter createResolvingArtifactFilter(String scope) { ArtifactFilter filter; // filter scope if (scope != null) { getLog().debug("+ Resolving dependency tree for scope '" + scope + "'"); filter = new ScopeArtifactFilter(scope); } else { filter = null; } return filter; } public void execute() throws MojoExecutionException { Log log = getLog(); Set artifacts = null; if (includeConflictingDepedencies) { try { artifacts = getDependencyArtifacts(project); } catch (Exception e) { throw new MojoExecutionException(e.getMessage(), e); } } else { artifacts = project.getArtifacts(); } try { // Create the target directory File root; if (targetDirectory == null) { root = new File(project.getBuild().getDirectory(), "plugins/"); } else { root = targetDirectory; } root.mkdirs(); // Build sets of exclude directories and included/excluded/groupids Set excludedFileNames = new HashSet(); if (excludeDirectories != null) { for (File f : excludeDirectories) { if (f.isDirectory()) { for (String n : f.list()) { excludedFileNames.add(n); } } } } Set includedGroupIds = new HashSet(); if (includeGroupIds != null) { for (String g : includeGroupIds) { includedGroupIds.add(g); } } Set excludedGroupIds = new HashSet(); if (excludeGroupIds != null) { for (String g : excludeGroupIds) { excludedGroupIds.add(g); } } // Find all the distribution poms List poms = new ArrayList(); poms.add(project); if (useDistributionName) { for (Object o : project.getArtifacts()) { Artifact artifact = (Artifact)o; if ("pom".equals(artifact.getType()) && artifact.getGroupId().equals(project.getGroupId()) && artifact.getArtifactId().startsWith("tuscany-feature-")) { log.info("Dependent distribution: " + artifact); MavenProject pomProject = buildProject(artifact); poms.add(pomProject); // log.info(pomProject.getArtifactMap().toString()); } } } // Process all the dependency artifacts ProjectSet bundleSymbolicNames = new ProjectSet(poms); ProjectSet bundleLocations = new ProjectSet(poms); ProjectSet jarNames = new ProjectSet(poms); ProjectSet serviceProviders = new ProjectSet(poms); for (Artifact artifact: artifacts) { // Only consider Compile and Runtime dependencies if (!(Artifact.SCOPE_COMPILE.equals(artifact.getScope()) || Artifact.SCOPE_RUNTIME.equals(artifact .getScope()) || Artifact.SCOPE_PROVIDED.equals(artifact.getScope()) || (generateTargetPlatform && Artifact.SCOPE_TEST .equals(artifact.getScope())))) { log.info("Skipping artifact: " + artifact); continue; } if (artifactTypes == null) { artifactTypes = "jar,bundle"; } String types[] = artifactTypes.trim().split("( |\t|\n|\r|\f|,)+"); Set typeSet = new HashSet(Arrays.asList(types)); // Only consider JAR and WAR files if (!typeSet.contains(artifact.getType())) { log.debug("Artifact with unknown type is skipped: " + artifact); continue; } // Exclude artifact if its groupId is excluded or if it's not included if (excludedGroupIds.contains(artifact.getGroupId())) { log.debug("Artifact groupId is excluded: " + artifact); continue; } if (!includedGroupIds.isEmpty()) { if (!includedGroupIds.contains(artifact.getGroupId())) { log.debug("Artifact groupId is not included: " + artifact); continue; } } File artifactFile = artifact.getFile(); if (!artifactFile.exists()) { log.warn("Artifact doesn't exist: " + artifact); continue; } if (log.isDebugEnabled()) { log.debug("Processing artifact: " + artifact); } Manifest customizedMF = findManifest(artifact); // Get the bundle name if the artifact is an OSGi bundle Manifest mf = null; String bundleName = null; try { mf = BundleUtil.getManifest(artifactFile); bundleName = BundleUtil.getBundleSymbolicName(mf); } catch (IOException e) { throw new MojoExecutionException(e.getMessage(), e); } if (bundleName != null && customizedMF == null) { // Exclude artifact if its file name is excluded if (excludedFileNames.contains(artifactFile.getName())) { log.debug("Artifact file is excluded: " + artifact); continue; } // Copy an OSGi bundle as is log.info("Adding OSGi bundle artifact: " + artifact); if (!expandThirdPartyBundle || artifact.getGroupId().startsWith("org.apache.tuscany.sca") || artifact.getGroupId().startsWith("org.eclipse")) { copyFile(artifactFile, root); bundleSymbolicNames.add(artifact, bundleName); bundleLocations.add(artifact, artifactFile.getName()); jarNames.add(artifact, artifactFile.getName()); if (isServiceProvider(mf)) { serviceProviders.add(artifact, bundleName); } } else { // Expanding the bundle into a folder setBundleClassPath(mf, artifactFile); int index = artifactFile.getName().lastIndexOf('.'); String dirName = artifactFile.getName().substring(0, index); File dir = new File(root, dirName); File file = new File(dir, "META-INF"); file.mkdirs(); file = new File(file, "MANIFEST.MF"); FileOutputStream fos = new FileOutputStream(file); write(mf, fos); fos.close(); copyFile(artifactFile, dir); bundleSymbolicNames.add(artifact, bundleName); bundleLocations.add(artifact, dir.getName()); jarNames.add(artifact, dirName + "/" + artifactFile.getName()); if (isServiceProvider(mf)) { serviceProviders.add(artifact, bundleName); } } } else if ("war".equals(artifact.getType())) { // Exclude artifact if its file name is excluded if (excludedFileNames.contains(artifactFile.getName())) { log.debug("Artifact file is excluded: " + artifact); continue; } // Copy a WAR as is log.info("Adding WAR artifact: " + artifact); copyFile(artifactFile, root); } else { int index = artifactFile.getName().lastIndexOf('.'); String dirName = artifactFile.getName().substring(0, index); File dir = new File(root, dirName); // Exclude artifact if its file name is excluded if (excludedFileNames.contains(dir.getName())) { log.debug("Artifact file is excluded: " + artifact); continue; } if (artifactAggregations != null) { boolean aggregated = false; for (ArtifactAggregation group : artifactAggregations) { if (group.matches(artifact)) { group.getArtifacts().add(artifact); aggregated = true; break; } } if (aggregated) { continue; } } // Create a bundle directory for a non-OSGi JAR log.info("Adding JAR artifact: " + artifact); // create manifest directory File file = new File(dir, "META-INF"); file.mkdirs(); String symbolicName = null; if (customizedMF == null) { String version = BundleUtil.osgiVersion(artifact.getVersion()); Set jarFiles = new HashSet(); jarFiles.add(artifactFile); symbolicName = (artifact.getGroupId() + "." + artifact.getArtifactId()); mf = BundleUtil.libraryManifest(jarFiles, symbolicName, symbolicName, version, null, this.eclipseBuddyPolicy, this.executionEnvironment); file = new File(file, "MANIFEST.MF"); FileOutputStream fos = new FileOutputStream(file); write(mf, fos); fos.close(); log.info("Writing generated manifest for: " + artifact + " to " + file); } else { mf = customizedMF; symbolicName = BundleUtil.getBundleSymbolicName(mf); if (symbolicName == null) { throw new MojoExecutionException("Invalid customized MANIFEST.MF for " + artifact); } setBundleClassPath(mf, artifactFile); // re-find the custom MF file and copy it // I can't get the manifest file from the manifest itself // the Manifest read/write operation seems to be filtering // out some entries that I've added manually???? File artifactManifest = null; if (artifactManifests != null) { for (ArtifactManifest m : artifactManifests) { if (m.matches(artifact)) { artifactManifest = m.getManifestFile(); break; } } } file = new File(file, "MANIFEST.MF"); if (artifactManifest != null){ log.info("Copying: " + artifactManifest + " to " + file); copyManifest(artifactManifest, file); } else { FileOutputStream fos = new FileOutputStream(file); write(mf, fos); fos.close(); log.info("Writing generated manifest for: " + artifact + " to " + file); } } copyFile(artifactFile, dir); bundleSymbolicNames.add(artifact, symbolicName); bundleLocations.add(artifact, dir.getName()); jarNames.add(artifact, dirName + "/" + artifactFile.getName()); if (isServiceProvider(mf)) { serviceProviders.add(artifact, symbolicName); } } } if (artifactAggregations != null) { for (ArtifactAggregation group : artifactAggregations) { if (group.getArtifacts().isEmpty()) { continue; } String symbolicName = group.getSymbolicName(); String version = group.getVersion(); File dir = new File(root, symbolicName + "-" + version); dir.mkdir(); Set jarFiles = new HashSet(); Artifact artifact = null; for (Artifact a : group.getArtifacts()) { log.info("Aggragating JAR artifact: " + a); artifact = a; jarFiles.add(a.getFile()); copyFile(a.getFile(), dir); jarNames.add(a, symbolicName + "-" + version + "/" + a.getFile().getName()); } Manifest mf = BundleUtil.libraryManifest(jarFiles, symbolicName, symbolicName, version, null, this.eclipseBuddyPolicy, this.executionEnvironment); File file = new File(dir, "META-INF"); file.mkdirs(); file = new File(file, "MANIFEST.MF"); FileOutputStream fos = new FileOutputStream(file); write(mf, fos); fos.close(); log.info("Written aggregate manifest"); bundleSymbolicNames.add(artifact, symbolicName); bundleLocations.add(artifact, dir.getName()); if (isServiceProvider(mf)) { serviceProviders.add(artifact, symbolicName); } } } if (generateGatewayBundle) { generateGatewayBundle(serviceProviders); } /* if (useDistributionName) { bundleLocations.nameMap.remove(project.getArtifactId()); jarNames.nameMap.remove(project.getArtifactId()); bundleSymbolicNames.nameMap.remove(project.getArtifactId()); } */ // Generate a PDE target if (generateTargetPlatform) { generatePDETarget(bundleSymbolicNames, root, log); } // Generate a plugin.xml referencing the PDE target if (generatePlugin) { File pluginxml = new File(project.getBasedir(), "plugin.xml"); FileOutputStream pluginXMLFile = new FileOutputStream(pluginxml); writePluginXML(new PrintStream(pluginXMLFile)); pluginXMLFile.close(); } if (generateConfig) { generateEquinoxConfig(bundleLocations, root, log); } if (generateManifestJar) { generateManifestJar(jarNames, root, log); generateEquinoxLauncherManifestJar(jarNames, root, log); } if (generateAntScript) { generateANTPath(jarNames, root, log); } if (generateAggregatedBundle) { generateAggregatedBundles(bundleLocations, root, log); } } catch (Exception e) { throw new MojoExecutionException(e.getMessage(), e); } } private Set getDependencyArtifacts(MavenProject project) throws DependencyTreeBuilderException, ArtifactResolutionException, ArtifactNotFoundException { Log log = getLog(); Set artifacts = new HashSet(); ArtifactFilter artifactFilter = createResolvingArtifactFilter(Artifact.SCOPE_RUNTIME); // TODO: note that filter does not get applied due to MNG-3236 DependencyNode rootNode = dependencyTreeBuilder.buildDependencyTree(project, local, factory, artifactMetadataSource, artifactFilter, artifactCollector); CollectingDependencyNodeVisitor visitor = new CollectingDependencyNodeVisitor(); rootNode.accept(visitor); // Add included artifacts for (Object node : visitor.getNodes()) { DependencyNode depNode = (DependencyNode)node; int state = depNode.getState(); if (state == DependencyNode.INCLUDED ) { Artifact artifact = depNode.getArtifact(); // Exclude the project artifact to avoid offline resolution failure if (!artifact.equals(project.getArtifact())) { resolver.resolve(artifact, remoteRepos, local); artifacts.add(artifact); } } } // Scan for newer versions that are omitted for (Object node : visitor.getNodes()) { DependencyNode depNode = (DependencyNode)node; int state = depNode.getState(); if (state == DependencyNode.OMITTED_FOR_CONFLICT) { Artifact artifact = depNode.getArtifact(); resolver.resolve(artifact, remoteRepos, local); if (state == DependencyNode.OMITTED_FOR_CONFLICT) { Artifact related = depNode.getRelatedArtifact(); if (log.isDebugEnabled()) { log.debug("Dependency node: " + depNode); } // Compare the version ArtifactVersion v1 = new DefaultArtifactVersion(artifact.getVersion()); ArtifactVersion v2 = new DefaultArtifactVersion(related.getVersion()); if (v1.compareTo(v2) > 0) { // Only add newer version if it is omitted for conflict if (artifacts.add(artifact)) { log.info("Dependency node added: " + depNode); } } } } } return artifacts; } private static boolean isServiceProvider(Manifest mf) { if (mf != null) { String export = (String)mf.getMainAttributes().getValue(Constants.EXPORT_PACKAGE); if (export != null && export.contains(BundleUtil.META_INF_SERVICES)) { return true; } } return false; } /** * Generate a gateway bundle that aggregate other bundles to handle split packages * @param bundleSymbolicNames * @throws FileNotFoundException * @throws IOException */ private void generateGatewayBundle(ProjectSet bundleSymbolicNames) throws FileNotFoundException, IOException { Manifest manifest = new Manifest(); Attributes attrs = manifest.getMainAttributes(); StringBuffer requireBundle = new StringBuffer(); for (String name : new HashSet(bundleSymbolicNames.artifactToNameMap.values())) { requireBundle.append(name).append(";").append(RESOLUTION_DIRECTIVE).append(":=") .append(RESOLUTION_OPTIONAL); if (gatewayReexport) { requireBundle.append(";").append(VISIBILITY_DIRECTIVE).append(":=").append(VISIBILITY_REEXPORT); } requireBundle.append(","); } int len = requireBundle.length(); if (len > 0 && requireBundle.charAt(len - 1) == ',') { requireBundle.deleteCharAt(len - 1); attrs.putValue(Constants.REQUIRE_BUNDLE, requireBundle.toString()); attrs.putValue("Manifest-Version", "1.0"); attrs.putValue("Implementation-Vendor", "The Apache Software Foundation"); attrs.putValue("Implementation-Vendor-Id", "org.apache"); attrs.putValue(Constants.BUNDLE_VERSION, "2.0.0"); attrs.putValue(Constants.BUNDLE_MANIFESTVERSION, "2"); attrs.putValue(Constants.BUNDLE_SYMBOLICNAME, GATEWAY_BUNDLE); attrs.putValue(Constants.BUNDLE_NAME, "Apache Tuscany SCA Gateway Bundle"); attrs.putValue(Constants.BUNDLE_VENDOR, "The Apache Software Foundation"); attrs.putValue(Constants.EXPORT_PACKAGE, "META-INF.services"); attrs.putValue(Constants.DYNAMICIMPORT_PACKAGE, "*"); attrs.putValue(Constants.BUNDLE_ACTIVATIONPOLICY, Constants.ACTIVATION_LAZY); File file = new File(targetDirectory, "tuscany-gateway-" + project.getVersion() + ".jar"); getLog().info("Generating gateway bundle: " + file.getAbsolutePath()); FileOutputStream fos = new FileOutputStream(file); JarOutputStream jos = new JarOutputStream(fos, manifest); addFileToJar(jos, "META-INF/LICENSE", getClass().getResource("LICENSE.txt")); addFileToJar(jos, "META-INF/NOTICE", getClass().getResource("NOTICE.txt")); jos.close(); } } private void setBundleClassPath(Manifest mf, File artifactFile) { // Add the Bundle-ClassPath String cp = mf.getMainAttributes().getValue(BUNDLE_CLASSPATH); if (cp == null) { cp = artifactFile.getName(); } else { cp = cp + "," + artifactFile.getName(); } mf.getMainAttributes().putValue(BUNDLE_CLASSPATH, cp); } private void generateANTPath(ProjectSet jarNames, File root, Log log) throws FileNotFoundException, IOException { for (Map.Entry> e : jarNames.nameMap.entrySet()) { Set jars = e.getValue(); File feature = new File(root, "../" + featuresName + "/" + (useDistributionName ? trim(e.getKey()) : "")); feature.mkdirs(); File antPath = new File(feature, "build-path.xml"); log.info("Generating ANT build path: " + antPath.getCanonicalPath()); FileOutputStream fos = new FileOutputStream(antPath); PrintStream ps = new PrintStream(fos); // ps.println(XML_PI); ps.println(ASL_HEADER); String name = trim(e.getKey()); ps.println(""); ps.println(" "); ps.println(" "); ps.println(" "); ps.println(" "); for (String jar : jars) { ps.println(" "); } ps.println(" "); ps.println(" "); ps.println(""); } } private void generateManifestJar(ProjectSet jarNames, File root, Log log) throws FileNotFoundException, IOException { for (Map.Entry> e : jarNames.nameMap.entrySet()) { MavenProject pom = jarNames.getProject(e.getKey()); Set jars = e.getValue(); File feature = new File(root, "../" + featuresName + "/" + (useDistributionName ? trim(e.getKey()) : "")); feature.mkdirs(); File mfJar = new File(feature, manifestJarName); log.info("Generating manifest jar: " + mfJar.getCanonicalPath()); FileOutputStream fos = new FileOutputStream(mfJar); Manifest mf = new Manifest(); StringBuffer cp = new StringBuffer(); String path = (useDistributionName ? "../../" : "../") + root.getName(); for (String jar : jars) { cp.append(path).append('/').append(jar).append(' '); } if (cp.length() > 0) { cp.deleteCharAt(cp.length() - 1); } Attributes attrs = mf.getMainAttributes(); attrs.putValue("Manifest-Version", "1.0"); attrs.putValue("Implementation-Title", pom.getName()); attrs.putValue("Implementation-Vendor", "The Apache Software Foundation"); attrs.putValue("Implementation-Vendor-Id", "org.apache"); attrs.putValue("Implementation-Version", pom.getVersion()); attrs.putValue("Class-Path", cp.toString()); attrs.putValue("Main-Class", "org.apache.tuscany.sca.node.launcher.NodeMain"); JarOutputStream jos = new JarOutputStream(fos, mf); addFileToJar(jos, "META-INF/LICENSE", getClass().getResource("LICENSE.txt")); addFileToJar(jos, "META-INF/NOTICE", getClass().getResource("NOTICE.txt")); jos.close(); } } private void generateEquinoxLauncherManifestJar(ProjectSet jarNames, File root, Log log) throws Exception { String equinoxLauncher = "org.apache.tuscany.sca:tuscany-node-launcher-equinox"; Artifact artifact = (Artifact)project.getArtifactMap().get(equinoxLauncher); if (artifact == null) { return; } Set artifacts = resolveTransitively(artifact).getArtifacts(); File feature = new File(root, "../" + featuresName + "/"); feature.mkdirs(); File mfJar = new File(feature, equinoxManifestJarName); log.info("Generating equinox manifest jar: " + mfJar.getCanonicalPath()); FileOutputStream fos = new FileOutputStream(mfJar); Manifest mf = new Manifest(); StringBuffer cp = new StringBuffer(); String path = "../" + root.getName(); for (Object o : artifacts) { Artifact a = (Artifact)o; if (!Artifact.SCOPE_TEST.equals(a.getScope())) { String id = ArtifactUtils.versionlessKey(a); String jar = jarNames.artifactToNameMap.get(id); if (jar != null) { cp.append(path).append('/').append(jar).append(' '); } } } if (cp.length() > 0) { cp.deleteCharAt(cp.length() - 1); } Attributes attrs = mf.getMainAttributes(); attrs.putValue("Manifest-Version", "1.0"); attrs.putValue("Implementation-Title", artifact.getId()); attrs.putValue("Implementation-Vendor", "The Apache Software Foundation"); attrs.putValue("Implementation-Vendor-Id", "org.apache"); attrs.putValue("Implementation-Version", artifact.getVersion()); attrs.putValue("Class-Path", cp.toString()); attrs.putValue("Main-Class", "org.apache.tuscany.sca.node.equinox.launcher.NodeMain"); JarOutputStream jos = new JarOutputStream(fos, mf); addFileToJar(jos, "META-INF/LICENSE", getClass().getResource("LICENSE.txt")); addFileToJar(jos, "META-INF/NOTICE", getClass().getResource("NOTICE.txt")); jos.close(); } private void generateEquinoxConfig(ProjectSet bundleLocations, File root, Log log) throws IOException { for (Map.Entry> e : bundleLocations.nameMap.entrySet()) { Set locations = new HashSet(e.getValue()); if (generateGatewayBundle) { locations.add("tuscany-gateway-" + project.getVersion() + ".jar"); } File feature = new File(root, "../" + featuresName + "/" + (useDistributionName ? trim(e.getKey()) : "")); File config = new File(feature, "configuration"); config.mkdirs(); File ini = new File(config, "config.ini"); log.info("Generating configuation: " + ini.getCanonicalPath()); FileOutputStream fos = new FileOutputStream(ini); PrintStream ps = new PrintStream(fos); int size = locations.size(); if (size > 0) { ps.println("osgi.bundles=\\"); int count = 0; for (String f : locations) { if (f.startsWith("osgi")) { count++; continue; } ps.print(" "); ps.print(f); // FIXME: We should not add @start for fragments if (generateBundleStart) { ps.print("@:start"); } if (count == size - 1) { // Last one ps.println(); } else { ps.println(",\\"); } count++; } } ps.println("eclipse.ignoreApp=true"); // Do not shutdown ps.println("osgi.noShutdown=true"); ps.close(); } } private void generateAggregatedBundles(ProjectSet bundleLocations, File root, Log log) throws Exception { for (Map.Entry> e : bundleLocations.nameMap.entrySet()) { Set locations = new HashSet(e.getValue()); String featureName = (useDistributionName ? trim(e.getKey()) : ""); File feature = new File(root, "../" + featuresName + "/" + featureName); // String bundleFileName = "tuscany-bundle-" + featureName + ".jar"; // if ("".equals(featureName)) { // bundleFileName = "tuscany-bundle.jar"; // } String bundleFileName = "tuscany-bundle.jar"; File bundleFile = new File(feature, bundleFileName); log.info("Generating aggregated OSGi bundle: " + bundleFile); File[] files = new File[locations.size()]; int i = 0; for (String child : locations) { files[i++] = new File(root, child); } String bundleVersion = "2.0.0"; String bundleName = "org.apache.tuscany.sca.bundle"; // String bundleName = "org.apache.tuscany.sca.bundle." + featureName; // if ("".equals(featureName)) { // bundleName = "org.apache.tuscany.sca.bundle"; // } BundleAggregatorMojo.aggregateBundles(log, root, files, bundleFile, bundleName, bundleVersion); } } private void generatePDETarget(ProjectSet bundleSymbolicNames, File root, Log log) throws FileNotFoundException, IOException { for (Map.Entry> e : bundleSymbolicNames.nameMap.entrySet()) { Set bundles = new HashSet(e.getValue()); String name = trim(e.getKey()); File feature = new File(root, "../" + featuresName + "/" + (useDistributionName ? name : "")); feature.mkdirs(); File target = new File(feature, "tuscany.target"); log.info("Generating target definition: " + target.getCanonicalPath()); FileOutputStream targetFile = new FileOutputStream(target); if (!bundles.contains("org.eclipse.osgi")) { bundles.add("org.eclipse.osgi"); } if (generateGatewayBundle) { bundles.add(GATEWAY_BUNDLE); } writeTarget(new PrintStream(targetFile), name, bundles, eclipseFeatures); targetFile.close(); // Generate the PDE target definition file for PDE 3.5 File target35 = new File(feature, "tuscany-pde35.target"); log.info("Generating target definition: " + target35.getCanonicalPath()); FileOutputStream target35File = new FileOutputStream(target35); writePDE35Target(new PrintStream(target35File), name, bundles, eclipseFeatures); target35File.close(); } } private MavenProject buildProject(Artifact artifact) throws ProjectBuildingException, InvalidDependencyVersionException, ArtifactResolutionException, ArtifactNotFoundException, DependencyTreeBuilderException { MavenProject pomProject = mavenProjectBuilder.buildFromRepository(artifact, this.remoteRepos, this.local); if (pomProject.getDependencyArtifacts() == null) { pomProject.setDependencyArtifacts(pomProject.createArtifacts(factory, null, // Artifact.SCOPE_TEST, new ScopeArtifactFilter(Artifact.SCOPE_TEST))); } if (includeConflictingDepedencies) { pomProject.setArtifacts(getDependencyArtifacts(pomProject)); } else { ArtifactResolutionResult result = resolver.resolveTransitively(pomProject.getDependencyArtifacts(), pomProject.getArtifact(), remoteRepos, local, artifactMetadataSource); pomProject.setArtifacts(result.getArtifacts()); } return pomProject; } private ArtifactResolutionResult resolveTransitively(Artifact artifact) throws ArtifactResolutionException, ArtifactNotFoundException { Artifact originatingArtifact = factory.createBuildArtifact("dummy", "dummy", "1.0", "jar"); return resolver.resolveTransitively(Collections.singleton(artifact), originatingArtifact, local, remoteRepos, artifactMetadataSource, null); } /** * Convert tuscany-feature-xyz to feature-xyz * @param artifactId * @return */ private String trim(String artifactId) { if (artifactId.startsWith("tuscany-feature-")) { return artifactId.substring("tuscany-feature-".length()); } else { return artifactId; } } private static void copyFile(File jar, File dir) throws FileNotFoundException, IOException { byte[] buf = new byte[4096]; File jarFile = new File(dir, jar.getName()); FileInputStream in = new FileInputStream(jar); FileOutputStream out = new FileOutputStream(jarFile); for (;;) { int len = in.read(buf); if (len > 0) { out.write(buf, 0, len); } else { break; } } in.close(); out.close(); } private static void copyManifest(File mfFrom, File mfTo) throws FileNotFoundException, IOException { byte[] buf = new byte[4096]; FileInputStream in = new FileInputStream(mfFrom); FileOutputStream out = new FileOutputStream(mfTo); for (;;) { int len = in.read(buf); if (len > 0) { out.write(buf, 0, len); } else { break; } } in.close(); out.close(); } private static void addFileToJar(JarOutputStream out, String entryName, URL file) throws FileNotFoundException, IOException { byte[] buf = new byte[4096]; InputStream in = file.openStream(); out.putNextEntry(new ZipEntry(entryName)); for (;;) { int len = in.read(buf); if (len > 0) { out.write(buf, 0, len); } else { break; } } in.close(); out.closeEntry(); } private void writeTarget(PrintStream ps, String pom, Set ids, String[] features) { ps.println(XML_PI); ps.println(""); ps.println(ASL_HEADER); ps.println(""); if (executionEnvironment != null) { ps.println(" "); ps.println(" " + executionEnvironment + ""); ps.println(" "); } if (useDefaultLocation) { ps.println(" "); } else { ps.println(" "); } // ps.println(""); ps.println(" "); ps.println(" "); for (String id : ids) { ps.println(" "); } ps.println(" "); ps.println(" "); if (features != null) { for (String f : features) { ps.println(" "); } } ps.println(" "); if (useDefaultLocation) { ps.println(" "); // Not sure why the extra path needs to the plugins folder ps.println(" "); ps.println(" "); } ps.println(" "); ps.println(""); } private void writePDE35Target(PrintStream ps, String pom, Set ids, String[] features) { ps.println(XML_PI); ps.println(""); ps.println(ASL_HEADER); ps.println(""); if (executionEnvironment != null) { ps .println(" "); } ps.println(""); if (ids.size() > 0) { ps.println(" "); ps.println(" "); for (String id : ids) { ps.println(" "); } ps.println(" "); ps.println(" "); } /* if (useDefaultLocation) { ps.println(" "); } */ /* if (features != null) { for (String f : features) { ps.println(" "); } } */ ps.println(""); ps.println(""); } private static void writePluginXML(PrintStream ps) { ps.println(XML_PI); ps.println(""); ps.println(ASL_HEADER); ps.println(""); ps.println(""); ps.println(""); ps.println(""); ps.println(""); } }