diff options
Diffstat (limited to 'maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin')
13 files changed, 4749 insertions, 0 deletions
diff --git a/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/AggregatedBundleActivator.java b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/AggregatedBundleActivator.java new file mode 100644 index 0000000000..64999cb309 --- /dev/null +++ b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/AggregatedBundleActivator.java @@ -0,0 +1,56 @@ +/* + * 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 java.util.ArrayList; +import java.util.List; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +/** + * A bundle activator that delegates to others + */ +public class AggregatedBundleActivator implements BundleActivator { + public static final String BUNDLE_ACTIVATOR_LIST = "Tuscany-Bundle-Activator-List"; + private List<BundleActivator> activators = new ArrayList<BundleActivator>(); + + public void start(BundleContext context) throws Exception { + String list = (String)context.getBundle().getHeaders().get(BUNDLE_ACTIVATOR_LIST); + if (list == null) { + return; + } + for (String cls : list.split(",")) { + Object i = context.getBundle().loadClass(cls).newInstance(); + if (i instanceof BundleActivator) { + ((BundleActivator)i).start(context); + activators.add((BundleActivator)i); + } + } + } + + public void stop(BundleContext context) throws Exception { + for (BundleActivator a : activators) { + a.stop(context); + } + + } + +} diff --git a/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/ArtifactAggregation.java b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/ArtifactAggregation.java new file mode 100644 index 0000000000..9abbe1ebd7 --- /dev/null +++ b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/ArtifactAggregation.java @@ -0,0 +1,72 @@ +/* + * 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 java.util.ArrayList; +import java.util.List; + +import org.apache.maven.artifact.Artifact; + +public class ArtifactAggregation { + private String symbolicName; + private String version; + private List<ArtifactMember> artifactMembers = new ArrayList<ArtifactMember>(); + private transient List<Artifact> artifacts = new ArrayList<Artifact>(); + + public List<Artifact> getArtifacts() { + return artifacts; + } + + public String getSymbolicName() { + return symbolicName; + } + + public void setSymbolicName(String symbolicName) { + this.symbolicName = symbolicName; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public List<ArtifactMember> getArtifactMembers() { + return artifactMembers; + } + + public void setArtifactMembers(List<ArtifactMember> artifacts) { + this.artifactMembers = artifacts; + } + + public String toString() { + return symbolicName + ";version=\"" + version + "\"\n" + artifactMembers; + } + + public boolean matches(Artifact artifact) { + for(ArtifactMember m: artifactMembers) { + if(m.matches(artifact)) { + return true; + } + } + return false; + } +}
\ No newline at end of file diff --git a/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/ArtifactManifest.java b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/ArtifactManifest.java new file mode 100644 index 0000000000..f6eca87c8e --- /dev/null +++ b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/ArtifactManifest.java @@ -0,0 +1,37 @@ +/* + * 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 java.io.File; + +/** + * + */ +public class ArtifactManifest extends ArtifactMember { + private File manifestFile; + + public File getManifestFile() { + return manifestFile; + } + + public void setManifestFile(File manifestFile) { + this.manifestFile = manifestFile; + } +} diff --git a/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/ArtifactMember.java b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/ArtifactMember.java new file mode 100644 index 0000000000..834ee7825e --- /dev/null +++ b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/ArtifactMember.java @@ -0,0 +1,61 @@ +/* + * 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 org.apache.maven.artifact.Artifact; + +public class ArtifactMember { + private String groupId; + private String artifactId; + private String version; + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getArtifactId() { + return artifactId; + } + + public void setArtifactId(String artifactId) { + this.artifactId = artifactId; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String toString() { + return groupId + ":" + artifactId + ":" + version; + } + + public boolean matches(Artifact artifact) { + return groupId.equals(artifact.getGroupId()) && (artifactId == null || artifactId.equals("") + || artifactId.equals("*") || artifactId.equals(artifact.getArtifactId())) + && (version == null || version.equals("") || version.equals("*") || version.equals(artifact.getVersion())); + } +} diff --git a/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/BundleAggregatorMojo.java b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/BundleAggregatorMojo.java new file mode 100644 index 0000000000..9c36ae34a2 --- /dev/null +++ b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/BundleAggregatorMojo.java @@ -0,0 +1,259 @@ +/* + * 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.AggregatedBundleActivator.BUNDLE_ACTIVATOR_LIST; +import static org.apache.tuscany.maven.bundle.plugin.HeaderParser.merge; +import static org.osgi.framework.Constants.BUNDLE_ACTIVATOR; +import static org.osgi.framework.Constants.BUNDLE_CLASSPATH; + +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.util.ArrayList; +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.JarFile; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.zip.ZipEntry; + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugin.logging.Log; +import org.apache.tuscany.maven.bundle.plugin.HeaderParser.HeaderClause; + +/** + * @version $Rev$ $Date$ + * @goal aggregate-modules + * @phase process-resources + * @requiresDependencyResolution test + * @description Generate an aggregated bundle that contains all the modules and 3rd party jars + */ +public class BundleAggregatorMojo extends AbstractMojo { + /** + * Root directory. + * + * @parameter expression="${project.build.directory}/modules" + */ + private File rootDirectory; + + /** + * Aggregated bundle + * + * @parameter expression="${project.build.directory}/singlebundle/tuscany-bundle.jar" + */ + private File targetBundleFile; + + /** + * @parameter default-value== "org.apache.tuscany.sca.bundle"; + */ + private String bundleName = "org.apache.tuscany.sca.bundle"; + + /** + * @parameter default-value== "2.0.0"; + */ + private String bundleVersion = "2.0.0"; + + // private static final Logger logger = Logger.getLogger(BundleAggregatorMojo.class.getName()); + + public static void aggregateBundles(Log log, + File root, + File[] files, + File targetBundleFile, + String bundleName, + String bundleVersion) throws Exception { + targetBundleFile.getParentFile().mkdirs(); + Set<File> jarFiles = new HashSet<File>(); + List<Manifest> manifests = new ArrayList<Manifest>(); + for (File child : files) { + try { + Manifest manifest = null; + if (child.isDirectory()) { + File mf = new File(child, "META-INF/MANIFEST.MF"); + if (mf.isFile()) { + FileInputStream is = new FileInputStream(mf); + manifest = new Manifest(is); + is.close(); + if (manifest != null) { + String classpath = manifest.getMainAttributes().getValue("Bundle-ClassPath"); + if (classpath != null) { + for (HeaderClause clause : HeaderParser.parse(classpath)) { + if (clause.getValue().equals(".")) { + continue; + } else { + jarFiles.add(new File(child, clause.getValue())); + } + } + } else { + // + } + } + } + } else if (child.getName().endsWith(".jar")) { + JarFile jar = new JarFile(child); + manifest = jar.getManifest(); + jar.close(); + if (manifest != null) { + String id = manifest.getMainAttributes().getValue("Bundle-SymbolicName"); + if (id != null && (id.startsWith("org.eclipse.") || id + .startsWith("org.apache.tuscany.sca.gateway"))) { + manifest = null; + } else { + jarFiles.add(child); + } + } + } + if (manifest == null) { + continue; + } + + log.debug("Bundle file: " + child); + manifests.add(manifest); + } catch (Exception e) { + throw e; + } + } + Manifest merged = new Manifest(); + Attributes attributes = merged.getMainAttributes(); + attributes.putValue("Manifest-Version", "1.0"); + attributes.putValue("Bundle-ManifestVersion", "2"); + attributes.putValue("Bundle-License", "http://www.apache.org/licenses/LICENSE-2.0.txt"); + attributes.putValue("Bundle-DocURL", "http://www.apache.org/"); + attributes.putValue("Bundle-RequiredExecutionEnvironment", "J2SE-1.5,JavaSE-1.6"); + attributes.putValue("Bundle-Vendor", "The Apache Software Foundation"); + attributes.putValue("Bundle-Version", bundleVersion); + attributes.putValue("Bundle-SymbolicName", bundleName); + attributes.putValue("SCA-Version", "1.1"); + attributes.putValue("Bundle-Name", bundleName); + // attributes.putValue("Bundle-ActivationPolicy", "lazy"); + for (Manifest mf : manifests) { + for (Map.Entry<Object, Object> e : mf.getMainAttributes().entrySet()) { + Attributes.Name key = (Attributes.Name)e.getKey(); + String name = key.toString(); + String oldValue = attributes.getValue(name); + String value = (String)e.getValue(); + if (name.equals("Export-Package") || name.equals("Import-Package") + || name.equals("Require-Bundle") + || name.equals("DynamicImport-Package") + || name.equals("Bundle-ClassPath") + || name.equals("Private-Package") + || name.equals("Bundle-Description")) { + attributes.putValue(name, merge(oldValue, value)); + } else if (name.equals(BUNDLE_ACTIVATOR)) { + oldValue = attributes.getValue(BUNDLE_ACTIVATOR_LIST); + attributes.putValue(BUNDLE_ACTIVATOR_LIST, merge(oldValue, value)); + } else if (name.equals("Main-Class") || name.startsWith("Eclipse-") || name.startsWith("Bundle-")) { + // Ignore + } else { + // Ignore + // attributes.putValue(name, value); + } + } + } + log.info("Generating " + targetBundleFile); + attributes.putValue(BUNDLE_ACTIVATOR, AggregatedBundleActivator.class.getName()); + String bundleClassPath = attributes.getValue(BUNDLE_CLASSPATH); + bundleClassPath = merge(bundleClassPath, "."); + for (File f : jarFiles) { + bundleClassPath = merge(bundleClassPath, f.getName()); + } + attributes.putValue(BUNDLE_CLASSPATH, bundleClassPath); + + FileOutputStream fos = new FileOutputStream(targetBundleFile); + JarOutputStream bundle = new JarOutputStream(fos, merged); + + for (File file : jarFiles) { + log.info("Adding " + file); + addEntry(bundle, file.getName(), file); + } + + String classFile = AggregatedBundleActivator.class.getName().replace(".", "/") + ".class"; + InputStream classStream = BundleAggregatorMojo.class.getClassLoader().getResourceAsStream(classFile); + addEntry(bundle, classFile, classStream); + bundle.close(); + } + + private static void addDir(JarOutputStream jos, File root, File dir) throws IOException, FileNotFoundException { + for (File file : dir.listFiles()) { + if (file.isDirectory()) { + addDir(jos, root, file); + } else if (file.isFile()) { + // getLog().info(file.toString()); + String uri = root.toURI().relativize(file.toURI()).toString(); + if ("META-INF/MANIFEST.MF".equals(uri)) { + continue; + } + addEntry(jos, uri, file); + } + } + } + + private static void addEntry(JarOutputStream jos, String name, File file) throws IOException { + FileInputStream in = new FileInputStream(file); + addEntry(jos, name, in); + } + + private static final byte[] buf = new byte[4096]; + + private static void addEntry(JarOutputStream jos, String name, InputStream in) throws IOException { + ZipEntry entry = new ZipEntry(name); + jos.putNextEntry(entry); + for (;;) { + int len = in.read(buf); + if (len > 0) { + jos.write(buf, 0, len); + } else { + break; + } + } + in.close(); + jos.closeEntry(); + } + + private static void generateJar(File root, File jar, Manifest mf) throws IOException { + FileOutputStream fos = new FileOutputStream(jar); + JarOutputStream jos = mf != null ? new JarOutputStream(fos, mf) : new JarOutputStream(fos); + addDir(jos, root, root); + jos.close(); + } + + public void execute() throws MojoExecutionException, MojoFailureException { + try { + Log log = getLog(); + if (!rootDirectory.isDirectory()) { + log.warn(rootDirectory + " is not a directory"); + return; + } + File[] files = rootDirectory.listFiles(); + aggregateBundles(log, rootDirectory, files, targetBundleFile, bundleName, bundleVersion); + } catch (Exception e) { + throw new MojoExecutionException(e.getMessage(), e); + } + + } +} diff --git a/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/BundleUtil.java b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/BundleUtil.java new file mode 100644 index 0000000000..8f280e59e9 --- /dev/null +++ b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/BundleUtil.java @@ -0,0 +1,640 @@ +/* + * 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.osgi.framework.Constants.BUNDLE_CLASSPATH; +import static org.osgi.framework.Constants.BUNDLE_MANIFESTVERSION; +import static org.osgi.framework.Constants.BUNDLE_NAME; +import static org.osgi.framework.Constants.BUNDLE_SYMBOLICNAME; +import static org.osgi.framework.Constants.BUNDLE_VERSION; +import static org.osgi.framework.Constants.DYNAMICIMPORT_PACKAGE; +import static org.osgi.framework.Constants.EXPORT_PACKAGE; +import static org.osgi.framework.Constants.IMPORT_PACKAGE; + +import java.io.BufferedInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Collections; +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.logging.Logger; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.apache.maven.artifact.versioning.ArtifactVersion; +import org.eclipse.osgi.framework.internal.core.Constants; +import org.eclipse.osgi.framework.internal.core.FrameworkProperties; +import org.eclipse.osgi.util.ManifestElement; +import org.osgi.framework.Version; + +/** + * Common functions used by the plugin. + * + * @version $Rev$ $Date$ + */ +public final class BundleUtil { + static final String META_INF_SERVICES = "META-INF.services;partial=true;mandatory:=partial"; + private final static Logger logger = Logger.getLogger(BundleUtil.class.getName()); + /** + * Returns the name of a bundle, or null if the given file is not a bundle. + * + * @param file + * @return + * @throws IOException + */ + public static String getBundleSymbolicName(File file) throws IOException { + Manifest manifest = getManifest(file); + return getBundleSymbolicName(manifest); + } + + static String getBundleSymbolicName(Manifest manifest) { + if (manifest == null) { + return null; + } + + String bundleName = manifest.getMainAttributes().getValue(BUNDLE_SYMBOLICNAME); + if (bundleName == null) { + return bundleName; + } + int sc = bundleName.indexOf(';'); + if (sc != -1) { + bundleName = bundleName.substring(0, sc); + } + return bundleName; + } + + static Manifest getManifest(File file) throws IOException { + if (!file.exists()) { + return null; + } + Manifest manifest = null; + if (file.isDirectory()) { + File mf = new File(file, "META-INF/MANIFEST.MF"); + if (mf.isFile()) { + InputStream is = new FileInputStream(mf); + manifest = new Manifest(new FileInputStream(mf)); + is.close(); + } + } else { + JarFile jar = new JarFile(file, false); + manifest = jar.getManifest(); + jar.close(); + } + return manifest; + } + + /** + * Generate a Bundle manifest for a set of JAR files. + * + * @param jarFiles + * @param name + * @param symbolicName + * @param version + * @param dir + * @return + * @throws IllegalStateException + */ + static Manifest libraryManifest(Set<File> jarFiles, String name, String symbolicName, String version, String dir) + throws IllegalStateException { + return libraryManifest(jarFiles, name, symbolicName, version, dir, null, null); + } + /** + * Generate a Bundle manifest for a set of JAR files. + * + * @param jarFiles + * @param name + * @param symbolicName + * @param version + * @param dir + * @param buddyPolicy + * @param env + * @return + * @throws IllegalStateException + */ + static Manifest libraryManifest(Set<File> jarFiles, String name, String symbolicName, String version, String dir, String buddyPolicy, String env) + throws IllegalStateException { + try { + + // List exported packages and bundle classpath entries + StringBuffer classpath = new StringBuffer(); + Set<String> exportedPackages = new HashSet<String>(); + for (File jarFile : jarFiles) { + if (!jarFile.exists()) { + logger.warning(jarFile + " doesn't exist."); + continue; + } + addPackages(jarFile, exportedPackages, version); + if (dir != null) { + classpath.append(dir).append("/"); + } + classpath.append(jarFile.getName()); + classpath.append(","); + } + + if (env == null) { + env = "JavaSE-1.6"; + } + Set<String> sysPackages = getSystemPackages(env); + + // Generate export-package and import-package declarations + StringBuffer exports = new StringBuffer(); + StringBuffer imports = new StringBuffer(); + Set<String> pkgs = new HashSet<String>(); + for (String export : exportedPackages) { + String packageName = packageName(export); + if (!pkgs.contains(packageName) && !sysPackages.contains(packageName)) { + // Add corresponding import declaration + if (!"META-INF.services".equals(packageName)) { + imports.append(export); + imports.append(','); + } + pkgs.add(packageName); + exports.append(export); + exports.append(','); + } else { + logger.warning("Duplicate or system package skipped: " + export); + } + } + + // Create a manifest + Manifest manifest = new Manifest(); + Attributes attributes = manifest.getMainAttributes(); + attributes.putValue("Manifest-Version", "1.0"); + attributes.putValue(BUNDLE_MANIFESTVERSION, "2"); + attributes.putValue(BUNDLE_SYMBOLICNAME, symbolicName); + attributes.putValue(BUNDLE_NAME, name); + attributes.putValue(BUNDLE_VERSION, version); + // The system bundle has incomplete javax.transaction* packages exported + attributes.putValue(DYNAMICIMPORT_PACKAGE, "javax.transaction;version=\"1.1\",javax.transaction.xa;version=\"1.1\",*"); + if (buddyPolicy != null && buddyPolicy.length() > 0){ + attributes.putValue("Eclipse-BuddyPolicy", buddyPolicy); + } + if (exports.length() > 1) { + attributes.putValue(EXPORT_PACKAGE, exports.substring(0, exports.length() - 1)); + } + /* + if (imports.length() > 1) { + attributes.putValue(IMPORT_PACKAGE, imports.substring(0, imports.length() - 1)); + } + */ + if (classpath.length() > 1) { + attributes.putValue(BUNDLE_CLASSPATH, classpath.substring(0, classpath.length() - 1)); + } + + return manifest; + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + /** + * Write a bundle manifest. + * + * @param manifest + * @param out + * @throws IOException + */ + static void write(Manifest manifest, OutputStream out) throws IOException { + DataOutputStream dos = new DataOutputStream(out); + Attributes attributes = manifest.getMainAttributes(); + write(attributes, "Manifest-Version", dos); + write(attributes, BUNDLE_MANIFESTVERSION, dos); + write(attributes, BUNDLE_SYMBOLICNAME, dos); + write(attributes, BUNDLE_NAME, dos); + write(attributes, BUNDLE_VERSION, dos); + write(attributes, DYNAMICIMPORT_PACKAGE, dos); + write(attributes, BUNDLE_CLASSPATH, dos); + write(attributes, IMPORT_PACKAGE, dos); + write(attributes, EXPORT_PACKAGE, dos); + write(attributes, "Eclipse-BuddyPolicy", dos); + dos.flush(); + } + + /** + * Add packages to be exported out of a JAR file. + * + * @param jarFile + * @param packages + * @throws IOException + */ + private static void addPackages(File jarFile, Set<String> packages, String version) throws IOException { +// if (getBundleSymbolicName(jarFile) == null) { + String ver = ";version=" + version; + addAllPackages(jarFile, packages, ver); +// } else { +// addExportedPackages(jarFile, packages); +// } + } + + /** + * Write manifest attributes. + * + * @param attributes + * @param key + * @param dos + * @throws IOException + */ + private static void write(Attributes attributes, String key, DataOutputStream dos) throws IOException { + String value = attributes.getValue(key); + if (value == null) { + return; + } + StringBuffer line = new StringBuffer(); + line.append(key); + line.append(": "); + line.append(new String(value.getBytes("UTF8"))); + line.append("\r\n"); + int l = line.length(); + if (l > 72) { + for (int i = 70; i < l - 2;) { + line.insert(i, "\r\n "); + i += 72; + l += 3; + } + } + dos.writeBytes(line.toString()); + } + + /** + * Strip an OSGi export, only retain the package name and version. + * + * @param export + * @return + */ + private static String stripExport(String export) { + int sc = export.indexOf(';'); + if (sc == -1) { + return export; + } + String base = export.substring(0, sc); + int v = export.indexOf("version="); + if (v != -1) { + sc = export.indexOf(';', v + 1); + if (sc != -1) { + return base + ";" + export.substring(v, sc); + } else { + return base + ";" + export.substring(v); + } + } else { + return base; + } + } + + /** + * Add all the packages out of a JAR. + * + * @param jarFile + * @param packages + * @param version + * @throws IOException + */ + private static void addAllPackages(File jarFile, Set<String> packages, String version) throws IOException { + ZipInputStream is = new ZipInputStream(new FileInputStream(jarFile)); + ZipEntry entry; + while ((entry = is.getNextEntry()) != null) { + String entryName = entry.getName(); + // Export split packages for META-INF/services + if(entryName.startsWith("META-INF/services/")) { + packages.add(META_INF_SERVICES); + } + if (!entry.isDirectory() && entryName != null + && entryName.length() > 0 + && !entryName.startsWith(".") + && entryName.endsWith(".class") // Exclude resources from Export-Package + && entryName.lastIndexOf("/") > 0 + && Character.isJavaIdentifierStart(entryName.charAt(0))) { + String pkg = entryName.substring(0, entryName.lastIndexOf("/")).replace('/', '.'); + if (!pkg.endsWith(".enum")) { + packages.add(pkg + version); + } + } + } + is.close(); + } + + private static Set<String> getSystemPackages(String env) throws IOException { + Set<String> sysPackages = new HashSet<String>(); + InputStream is = BundleUtil.class.getResourceAsStream("/" + env + ".profile"); + if (is != null) { + Properties props = new Properties(); + props.load(is); + String pkgs = (String)props.get("org.osgi.framework.system.packages"); + if (pkgs != null) { + for (String p : pkgs.split(",")) { + sysPackages.add(p.trim()); + } + } + } + return sysPackages; + } + + /** + * Returns the name of the exported package in the given export. + * @param export + * @return + */ + private static String packageName(String export) { + int sc = export.indexOf(';'); + if (sc != -1) { + export = export.substring(0, sc); + } + return export; + } + + /** + * Add the packages exported by a bundle. + * + * @param file + * @param packages + * @return + * @throws IOException + */ + public static Set<String> getExportedPackages(File file) throws IOException { + if (!file.exists()) { + return Collections.emptySet(); + } + + Set<String> packages = new HashSet<String>(); + Manifest manifest = getManifest(file); + + // Read the export-package declaration and get a list of the packages available in a JAR + String bundleName = null; + String exports = null; + + if (manifest != null) { + exports = manifest.getMainAttributes().getValue(EXPORT_PACKAGE); + bundleName = manifest.getMainAttributes().getValue(BUNDLE_SYMBOLICNAME); + } + + if (bundleName == null) { + Set<String> allPackages = new HashSet<String>(); + addAllPackages(file, allPackages, ""); + for (String p : allPackages) { + packages.add(packageName(p)); + } + return packages; + } + + packages.addAll(parsePackages(exports)); + + return packages; + } + + public static Set<String> getImportedPackages(File file) throws IOException { + if (!file.exists()) { + return Collections.emptySet(); + } + + Manifest manifest = getManifest(file); + + // Read the export-package declaration and get a list of the packages available in a JAR + String bundleName = null; + String imports = null; + + if (manifest != null) { + imports = manifest.getMainAttributes().getValue(IMPORT_PACKAGE); + bundleName = manifest.getMainAttributes().getValue(BUNDLE_SYMBOLICNAME); + if (imports != null && bundleName != null) { + return parsePackages(imports); + } + } + return Collections.emptySet(); + } + + private static Set<String> parsePackages(String header) { + if (header == null) { + return Collections.emptySet(); + } + Set<String> packages = new HashSet<String>(); + // Parse the export-package declaration, and extract the individual packages + StringBuffer buffer = new StringBuffer(); + boolean q = false; + for (int i = 0, n = header.length(); i < n; i++) { + char c = header.charAt(i); + if (c == '\"') { + q = !q; + } + if (!q) { + if (c == ',') { + + // Add the exported package to the set, after making sure it really exists in + // the JAR + String export = buffer.toString(); + packages.add(packageName(export)); + buffer = new StringBuffer(); + continue; + } + } + buffer.append(c); + } + if (buffer.length() != 0) { + String export = buffer.toString(); + packages.add(packageName(export)); + } + return packages; + } + + /** + * Convert the maven version into OSGi version + * @param mavenVersion + * @return + */ + static String osgiVersion(String mavenVersion) { + ArtifactVersion ver = new OSGIArtifactVersion(mavenVersion); + String qualifer = ver.getQualifier(); + if (qualifer != null) { + StringBuffer buf = new StringBuffer(qualifer); + for (int i = 0; i < buf.length(); i++) { + char c = buf.charAt(i); + if (Character.isLetterOrDigit(c) || c == '-' || c == '_') { + // Keep as-is + } else { + buf.setCharAt(i, '_'); + } + } + qualifer = buf.toString(); + } + Version osgiVersion = + new Version(ver.getMajorVersion(), ver.getMinorVersion(), ver.getIncrementalVersion(), qualifer); + String version = osgiVersion.toString(); + return version; + } + + private static String J2SE = "J2SE-"; + private static String JAVASE = "JavaSE-"; + private static String PROFILE_EXT = ".profile"; + private static URL findInSystemBundle(String entry) { + ClassLoader loader = BundleUtil.class.getClassLoader(); + return loader == null ? ClassLoader.getSystemResource(entry) : loader.getResource(entry); + } + + private static URL findNextBestProfile(String javaEdition, Version javaVersion) { + URL result = null; + int minor = javaVersion.getMinor(); + do { + result = findInSystemBundle(javaEdition + javaVersion.getMajor() + "." + minor + PROFILE_EXT); + minor = minor - 1; + } while (result == null && minor > 0); + return result; + } + + private static Properties findVMProfile(Properties properties) { + Properties result = new Properties(); + // Find the VM profile name using J2ME properties + String j2meConfig = properties.getProperty(Constants.J2ME_MICROEDITION_CONFIGURATION); + String j2meProfiles = properties.getProperty(Constants.J2ME_MICROEDITION_PROFILES); + String vmProfile = null; + String javaEdition = null; + Version javaVersion = null; + if (j2meConfig != null && j2meConfig.length() > 0 && j2meProfiles != null && j2meProfiles.length() > 0) { + // save the vmProfile based off of the config and profile + // use the last profile; assuming that is the highest one + String[] j2meProfileList = ManifestElement.getArrayFromList(j2meProfiles, " "); + if (j2meProfileList != null && j2meProfileList.length > 0) + vmProfile = j2meConfig + '_' + j2meProfileList[j2meProfileList.length - 1]; + } else { + // No J2ME properties; use J2SE properties + // Note that the CDC spec appears not to require VM implementations to set the + // javax.microedition properties!! So we will try to fall back to the + // java.specification.name property, but this is pretty ridiculous!! + String javaSpecVersion = properties.getProperty("java.specification.version"); + // set the profile and EE based off of the java.specification.version + // TODO We assume J2ME Foundation and J2SE here. need to support other profiles J2EE ... + if (javaSpecVersion != null) { + StringTokenizer st = new StringTokenizer(javaSpecVersion, " _-"); + javaSpecVersion = st.nextToken(); + String javaSpecName = properties.getProperty("java.specification.name"); + if ("J2ME Foundation Specification".equals(javaSpecName)) + vmProfile = "CDC-" + javaSpecVersion + "_Foundation-" + javaSpecVersion; //$NON-NLS-2$ + else { + // look for JavaSE if 1.6 or greater; otherwise look for J2SE + Version v16 = new Version("1.6"); + javaEdition = J2SE; + try { + javaVersion = new Version(javaSpecVersion); + if (v16.compareTo(javaVersion) <= 0) + javaEdition = JAVASE; + } catch (IllegalArgumentException e) { + // do nothing + } + vmProfile = javaEdition + javaSpecVersion; + } + } + } + URL url = null; + // check for the java profile property for a url + String propJavaProfile = FrameworkProperties.getProperty(Constants.OSGI_JAVA_PROFILE); + if (propJavaProfile != null) + try { + // we assume a URL + url = new URL(propJavaProfile); + } catch (MalformedURLException e1) { + // try using a relative path in the system bundle + url = findInSystemBundle(propJavaProfile); + } + if (url == null && vmProfile != null) { + // look for a profile in the system bundle based on the vm profile + String javaProfile = vmProfile + PROFILE_EXT; + url = findInSystemBundle(javaProfile); + if (url == null) + url = getNextBestProfile(javaEdition, javaVersion); + } + if (url == null) + // the profile url is still null then use the osgi min profile in OSGi by default + url = findInSystemBundle("OSGi_Minimum-1.1.profile"); + if (url != null) { + InputStream in = null; + try { + in = url.openStream(); + result.load(new BufferedInputStream(in)); + } catch (IOException e) { + // TODO consider logging ... + } finally { + if (in != null) + try { + in.close(); + } catch (IOException ee) { + // do nothing + } + } + } + // set the profile name if it does not provide one + if (result.getProperty(Constants.OSGI_JAVA_PROFILE_NAME) == null) + if (vmProfile != null) + result.put(Constants.OSGI_JAVA_PROFILE_NAME, vmProfile.replace('_', '/')); + else + // last resort; default to the absolute minimum profile name for the framework + result.put(Constants.OSGI_JAVA_PROFILE_NAME, "OSGi/Minimum-1.1"); + return result; + } +/* doesn't work with Equinox 3.5.0 + public static void loadVMProfile(Properties properties) { + Properties profileProps = findVMProfile(properties); + String systemExports = properties.getProperty(Constants.OSGI_FRAMEWORK_SYSTEM_PACKAGES); + // set the system exports property using the vm profile; only if the property is not already set + if (systemExports == null) { + systemExports = profileProps.getProperty(Constants.OSGI_FRAMEWORK_SYSTEM_PACKAGES); + if (systemExports != null) + properties.put(Constants.OSGI_FRAMEWORK_SYSTEM_PACKAGES, systemExports); + } + // set the org.osgi.framework.bootdelegation property according to the java profile + String type = properties.getProperty(Constants.OSGI_JAVA_PROFILE_BOOTDELEGATION); // a null value means ignore + String profileBootDelegation = profileProps.getProperty(Constants.OSGI_BOOTDELEGATION); + if (Constants.OSGI_BOOTDELEGATION_OVERRIDE.equals(type)) { + if (profileBootDelegation == null) + properties.remove(Constants.OSGI_BOOTDELEGATION); // override with a null value + else + properties.put(Constants.OSGI_BOOTDELEGATION, profileBootDelegation); // override with the profile value + } else if (Constants.OSGI_BOOTDELEGATION_NONE.equals(type)) + properties.remove(Constants.OSGI_BOOTDELEGATION); // remove the bootdelegation property in case it was set + // set the org.osgi.framework.executionenvironment property according to the java profile + if (properties.getProperty(Constants.FRAMEWORK_EXECUTIONENVIRONMENT) == null) { + // get the ee from the java profile; if no ee is defined then try the java profile name + String ee = + profileProps.getProperty(Constants.FRAMEWORK_EXECUTIONENVIRONMENT, profileProps + .getProperty(Constants.OSGI_JAVA_PROFILE_NAME)); + if (ee != null) + properties.put(Constants.FRAMEWORK_EXECUTIONENVIRONMENT, ee); + } + } +*/ + + private static URL getNextBestProfile(String javaEdition, Version javaVersion) { + if (javaVersion == null || (javaEdition != J2SE && javaEdition != JAVASE)) + return null; // we cannot automatically choose the next best profile unless this is a J2SE or JavaSE vm + URL bestProfile = findNextBestProfile(javaEdition, javaVersion); + if (bestProfile == null && javaEdition == JAVASE) + // if this is a JavaSE VM then search for a lower J2SE profile + bestProfile = findNextBestProfile(J2SE, javaVersion); + return bestProfile; + } + +} diff --git a/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/BundlesMetaDataBuildMojo.java b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/BundlesMetaDataBuildMojo.java new file mode 100644 index 0000000000..8d6cfd9aa4 --- /dev/null +++ b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/BundlesMetaDataBuildMojo.java @@ -0,0 +1,1465 @@ +/*
+ * 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.TreeSet;
+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 meta-data for groups of Tuscany jars from the distribution's
+ * modules directory. The meta-data group jars either as mnaifest jars, ant filesets or
+ * simply as human readable lists of jars
+ *
+ * @version $Rev: 929729 $ $Date: 2010-03-31 22:52:37 +0100 (Wed, 31 Mar 2010) $
+ * @goal generate-meta-data
+ * @phase generate-resources
+ * @requiresDependencyResolution test
+ * @description Generate a modules directory containing OSGi bundles for all the project's module dependencies.
+ */
+public class BundlesMetaDataBuildMojo 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 = false;
+
+ /**
+ * Set to true to generate the contents of the modules directory.
+ *
+ * @parameter default-value="true"
+ */
+ private boolean generateModules = false;
+
+ /**
+ * 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-<version>.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 default-value="true"
+ */
+ private boolean generateWhichJars = true;
+
+ /**
+ * @parameter
+ */
+ private ArtifactAggregation[] artifactAggregations;
+
+ /**
+ * @parameter
+ */
+ private ArtifactManifest[] artifactManifests;
+
+ /**
+ * Feature artifact IDs to be processed and included in the features directory
+ * @parameter
+ */
+ private Feature[] features;
+
+ /**
+ * Extension artifact IDs to be processed and included in the extensions directory
+ * @parameter
+ */
+ private Extension[] extensions;
+
+ /**
+ * Inserts a generic Eclipse-BuddyPolicy header into generated artifacts manifests
+ * @parameter
+ */
+ private String eclipseBuddyPolicy = null;
+
+ private static final String XML_PI = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+ private static final String ASL_HEADER =
+ "<!--" + "\n * Licensed to the Apache Software Foundation (ASF) under one"
+ + "\n * or more contributor license agreements. See the NOTICE file"
+ + "\n * distributed with this work for additional information"
+ + "\n * regarding copyright ownership. The ASF licenses this file"
+ + "\n * to you under the Apache License, Version 2.0 (the"
+ + "\n * \"License\"); you may not use this file except in compliance"
+ + "\n * with the License. You may obtain a copy of the License at"
+ + "\n * "
+ + "\n * http://www.apache.org/licenses/LICENSE-2.0"
+ + "\n * "
+ + "\n * Unless required by applicable law or agreed to in writing,"
+ + "\n * software distributed under the License is distributed on an"
+ + "\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY"
+ + "\n * KIND, either express or implied. See the License for the"
+ + "\n * specific language governing permissions and limitations"
+ + "\n * under the License."
+ + "\n-->";
+
+ /**
+ * Group the artifacts by distribution poms
+ */
+ private class ProjectSet {
+ // Distribution projects
+ private Map<String, MavenProject> projects;
+ // Key: the pom artifact id
+ // Value: the names for the artifacts
+ private Map<String, Set<String>> nameMap = new HashMap<String, Set<String>>();
+
+ private Map<String, String> artifactToNameMap = new HashMap<String, String>();
+
+ public ProjectSet(List<MavenProject> projects) {
+ super();
+ this.projects = new HashMap<String, MavenProject>();
+ 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 &&
+ artifact.getArtifactId().equals(p.getArtifactId())){
+ a = p.getArtifact();
+ }
+ if (a != null) {
+ if (a.getScope() != null &&
+ (a.getScope().equals("provided") ||
+ a.getScope().equals("test") ||
+ a.getScope().equals("system"))){
+ // ignore the artifact
+ } else {
+ Set<String> names = nameMap.get(p.getArtifactId());
+ if (names == null) {
+ names = new TreeSet<String>();
+ 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<File> jarFiles = new HashSet<File>();
+ 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<Artifact> 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;
+ }
+
+ if (generateModules){
+ root.mkdirs();
+ }
+
+ // Build sets of exclude directories and included/excluded/groupids
+ Set<String> excludedFileNames = new HashSet<String>();
+ if (excludeDirectories != null) {
+ for (File f : excludeDirectories) {
+ if (f.isDirectory()) {
+ for (String n : f.list()) {
+ excludedFileNames.add(n);
+ }
+ }
+ }
+ }
+ Set<String> includedGroupIds = new HashSet<String>();
+ if (includeGroupIds != null) {
+ for (String g : includeGroupIds) {
+ includedGroupIds.add(g);
+ }
+ }
+ Set<String> excludedGroupIds = new HashSet<String>();
+ if (excludeGroupIds != null) {
+ for (String g : excludeGroupIds) {
+ excludedGroupIds.add(g);
+ }
+ }
+
+ // Find all the distribution poms
+ List<MavenProject> poms = new ArrayList<MavenProject>();
+
+ 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());
+ }
+ }
+ }
+
+ if (features != null){
+ // find all the features that require processing
+ for (Object o : project.getArtifacts()) {
+ Artifact artifact = (Artifact)o;
+ for(Feature feature : features){
+ if (artifact.getGroupId().equals(feature.getGroupId()) &&
+ artifact.getArtifactId().equals(feature.getArtifactId())) {
+ log.info("Feature: " + artifact);
+ MavenProject pomProject = buildProject(artifact);
+ poms.add(pomProject);
+ }
+ }
+ }
+
+ // force useDistributionName to true so that subsequent generation works
+ useDistributionName = true;
+ }
+
+ if (extensions != null){
+ // find all the extensions that require processing
+ // TODO - putting them in features dir for the time being
+ for (Object o : project.getArtifacts()) {
+ Artifact artifact = (Artifact)o;
+ for(Extension extension : extensions) {
+ if (artifact.getGroupId().equals(extension.getGroupId()) &&
+ artifact.getArtifactId().equals(extension.getArtifactId())) {
+ log.info("Extension: " + artifact);
+ MavenProject pomProject = buildProject(artifact);
+ poms.add(pomProject);
+ }
+ }
+ }
+
+ // force useDistributionName to true so that subsequent generation works
+ useDistributionName = true;
+ }
+
+ // If no features have been specified assume that the current
+ // project defines the feature
+ if (poms.size() == 0){
+ poms.add(project);
+ }
+
+ // 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) {
+
+ log.info("Processing artifact: " + artifact);
+
+ // 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<String> typeSet = new HashSet<String>(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;
+ }
+
+ if (generateModules){
+ // 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")) {
+ if (generateModules){
+ 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);
+ if (generateModules){
+ 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;
+ }
+
+ if (generateModules){
+ // 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 manifest directory
+ File file = new File(dir, "META-INF");
+ if (generateModules){
+ // Create a bundle directory for a non-OSGi JAR
+ log.info("Adding JAR artifact: " + artifact);
+
+ file.mkdirs();
+ }
+
+ String symbolicName = null;
+ if (customizedMF == null) {
+ String version = BundleUtil.osgiVersion(artifact.getVersion());
+
+ Set<File> jarFiles = new HashSet<File>();
+ jarFiles.add(artifactFile);
+ symbolicName = (artifact.getGroupId() + "." + artifact.getArtifactId());
+ if (generateModules){
+ 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;
+ }
+ }
+ }
+
+ if (generateModules){
+ 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);
+ }
+ }
+ }
+
+ if (generateModules){
+ 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<File> jarFiles = new HashSet<File>();
+ 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 (generateWhichJars) {
+ generateWhichJars(jarNames, root, log);
+ }
+
+ if (generateAggregatedBundle) {
+ generateAggregatedBundles(bundleLocations, root, log);
+ }
+
+ } catch (Exception e) {
+ throw new MojoExecutionException(e.getMessage(), e);
+ }
+
+ }
+
+ private Set<Artifact> getDependencyArtifacts(MavenProject project) throws DependencyTreeBuilderException,
+ ArtifactResolutionException, ArtifactNotFoundException {
+ Log log = getLog();
+ Set<Artifact> artifacts = new HashSet<Artifact>();
+ 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<String>(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<String, Set<String>> e : jarNames.nameMap.entrySet()) {
+ Set<String> 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("<project name=\"" + name + "\">");
+ ps.println(" <property name=\"tuscany.manifest\" value=\"" + new File(feature, manifestJarName).getCanonicalPath() + "\"/>");
+ ps.println(" <dirname property=\"" + name + ".basedir\" file=\"${ant.file." + name + "}\"/>");
+ ps.println(" <path id=\"" + name + ".path" + "\">");
+ ps.println(" <fileset dir=\"${" + name + ".basedir}../../../modules\">");
+ for (String jar : jars) {
+ ps.println(" <include name=\"" + jar + "\"/>");
+ }
+ ps.println(" </fileset>");
+ ps.println(" </path>");
+ ps.println("</project>");
+ }
+ }
+
+ private void generateWhichJars(ProjectSet jarNames, File root, Log log) throws FileNotFoundException, IOException {
+ for (Map.Entry<String, Set<String>> e : jarNames.nameMap.entrySet()) {
+ Set<String> jars = e.getValue();
+ File feature = new File(root, "../" + featuresName + "/" + (useDistributionName ? trim(e.getKey()) : ""));
+ feature.mkdirs();
+ File whichJarsPath = new File(feature, "which-jars");
+ log.info("Generating Which Jars: " + whichJarsPath.getCanonicalPath());
+ FileOutputStream fos = new FileOutputStream(whichJarsPath);
+ PrintStream ps = new PrintStream(fos);
+
+ ps.println(ASL_HEADER);
+ String name = trim(e.getKey());
+ ps.println("Jars required to enable extension: " + name);
+ ps.println("");
+ for (String jar : jars) {
+ ps.println(jar);
+ }
+ }
+ }
+
+ private void generateManifestJar(ProjectSet jarNames, File root, Log log) throws FileNotFoundException, IOException {
+ for (Map.Entry<String, Set<String>> e : jarNames.nameMap.entrySet()) {
+ MavenProject pom = jarNames.getProject(e.getKey());
+ Set<String> jars = e.getValue();
+ String name = trim(e.getKey());
+ File feature = new File(root, "../" + featuresName + "/" + (useDistributionName ? trim(e.getKey()) : ""));
+ feature.mkdirs();
+ String manifestName = name + "-manifest.jar";
+ File mfJar = new File(feature, manifestName);
+ 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<String, Set<String>> e : bundleLocations.nameMap.entrySet()) {
+ Set<String> locations = new HashSet<String>(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<String, Set<String>> e : bundleLocations.nameMap.entrySet()) {
+ Set<String> locations = new HashSet<String>(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<String, Set<String>> e : bundleSymbolicNames.nameMap.entrySet()) {
+ Set<String> bundles = new HashSet<String>(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<String> ids, String[] features) {
+ ps.println(XML_PI);
+ ps.println("<?pde version=\"3.2\"?>");
+ ps.println(ASL_HEADER);
+
+ ps.println("<target name=\"Eclipse Target - " + pom + "\">");
+
+ if (executionEnvironment != null) {
+ ps.println(" <targetJRE>");
+ ps.println(" <execEnv>" + executionEnvironment + "</execEnv>");
+ ps.println(" </targetJRE>");
+ }
+
+ if (useDefaultLocation) {
+ ps.println(" <location useDefault=\"true\"/>");
+ } else {
+ ps.println(" <location path=\"" + targetDirectory + "\"/>");
+ }
+
+ // ps.println("<content useAllPlugins=\"true\">");
+ ps.println(" <content>");
+ ps.println(" <plugins>");
+ for (String id : ids) {
+ ps.println(" <plugin id=\"" + id + "\"/>");
+ }
+ ps.println(" </plugins>");
+ ps.println(" <features>");
+ if (features != null) {
+ for (String f : features) {
+ ps.println(" <feature id=\"" + f + "\"/>");
+ }
+ }
+ ps.println(" </features>");
+ if (useDefaultLocation) {
+ ps.println(" <extraLocations>");
+ // Not sure why the extra path needs to the plugins folder
+ ps.println(" <location path=\"" + targetDirectory + "\"/>");
+ ps.println(" </extraLocations>");
+ }
+ ps.println(" </content>");
+
+ ps.println("</target>");
+
+ }
+
+ private void writePDE35Target(PrintStream ps, String pom, Set<String> ids, String[] features) {
+ ps.println(XML_PI);
+ ps.println("<?pde version=\"3.5\"?>");
+ ps.println(ASL_HEADER);
+
+ ps.println("<target name=\"Eclipse PDE 3.5 Target - " + pom + "\">");
+
+ if (executionEnvironment != null) {
+ ps
+ .println(" <targetJRE path=\"" + "org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/"
+ + executionEnvironment
+ + "\"/>");
+ }
+
+ ps.println("<locations>");
+ if (ids.size() > 0) {
+ ps.println(" <location path=\"" + targetDirectory + "\" type=\"Directory\">");
+ ps.println(" <includeBundles>");
+ for (String id : ids) {
+ ps.println(" <plugin id=\"" + id + "\"/>");
+ }
+ ps.println(" </includeBundles>");
+ ps.println(" </location>");
+ }
+
+ /*
+ if (useDefaultLocation) {
+ ps.println(" <location path=\"${eclipse_home}\" type=\"Profile\"/>");
+ }
+ */
+
+ /*
+ if (features != null) {
+ for (String f : features) {
+ ps.println(" <location id=\"" + f + "\" path=\"\" type=\"Feature\"/>");
+ }
+ }
+ */
+
+ ps.println("</locations>");
+ ps.println("</target>");
+
+ }
+
+ private static void writePluginXML(PrintStream ps) {
+ ps.println(XML_PI);
+ ps.println("<?pde version=\"3.2\"?>");
+ ps.println(ASL_HEADER);
+ ps.println("<plugin>");
+ ps.println("<extension point = \"org.eclipse.pde.core.targets\">");
+ ps.println("<target");
+ ps.println("id=\"org.apache.tuscany.sca.target\"");
+ ps.println("name=\"Apache Tuscany Eclipse Target\"");
+ ps.println("path=\"tuscany.target\"/>");
+ ps.println("</extension>");
+ ps.println("</plugin>");
+ }
+}
diff --git a/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/Extension.java b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/Extension.java new file mode 100644 index 0000000000..dd4a9b502c --- /dev/null +++ b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/Extension.java @@ -0,0 +1,60 @@ +/*
+ * 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;
+
+
+public class Extension {
+ private String groupId;
+ private String artifactId;
+ private String version;
+
+ public String getGroupId() {
+ return groupId;
+ }
+
+ public void setGroupId(String groupId) {
+ this.groupId = groupId;
+ }
+
+ public String getArtifactId() {
+ return artifactId;
+ }
+
+ public void setArtifactId(String artifactId) {
+ this.artifactId = artifactId;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public String toString() {
+ return groupId + ":" + artifactId + ":" + version;
+ }
+
+ public boolean matches(Extension extension) {
+ return groupId.equals(extension.getGroupId()) && (artifactId == null || artifactId.equals("")
+ || artifactId.equals("*") || artifactId.equals(extension.getArtifactId()))
+ && (version == null || version.equals("") || version.equals("*") || version.equals(extension.getVersion()));
+ }
+}
diff --git a/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/Feature.java b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/Feature.java new file mode 100644 index 0000000000..41d3b2dc44 --- /dev/null +++ b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/Feature.java @@ -0,0 +1,59 @@ +/*
+ * 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;
+
+public class Feature {
+ private String groupId;
+ private String artifactId;
+ private String version;
+
+ public String getGroupId() {
+ return groupId;
+ }
+
+ public void setGroupId(String groupId) {
+ this.groupId = groupId;
+ }
+
+ public String getArtifactId() {
+ return artifactId;
+ }
+
+ public void setArtifactId(String artifactId) {
+ this.artifactId = artifactId;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public String toString() {
+ return groupId + ":" + artifactId + ":" + version;
+ }
+
+ public boolean matches(Feature feature) {
+ return groupId.equals(feature.getGroupId()) && (artifactId == null || artifactId.equals("")
+ || artifactId.equals("*") || artifactId.equals(feature.getArtifactId()))
+ && (version == null || version.equals("") || version.equals("*") || version.equals(feature.getVersion()));
+ }
+}
diff --git a/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/HeaderParser.java b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/HeaderParser.java new file mode 100644 index 0000000000..c49261e3ff --- /dev/null +++ b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/HeaderParser.java @@ -0,0 +1,331 @@ +/* + * 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 java.util.ArrayList; +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 javax.xml.namespace.QName; + +/** + * Parser for the service descriptors. The syntax of the service declaration is similar with the OSGi + * headers with the following exceptions: + * <ul> + * <li>Tuscany uses , and ; as the separator for attibutes + * <li>Tuscany + */ +public class HeaderParser { + + private static final String PATH_SEPARATOR = ","; // OSGi style + // private static final String PATH_SEPARATOR = "|"; + + private static final String SEGMENT_SEPARATOR = ";"; // OSGi style + // private static final String SEGMENT_SEPARATOR = ";,"; + + private static final String ATTRIBUTE_SEPARATOR = "="; + private static final String DIRECTIVE_SEPARATOR = ":="; + + private static final char QUOTE_CHAR = '"'; + private static final String QUOTE = "\""; + + // Like this: path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2, + // path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2 + public static List<HeaderClause> parse(String header) { + + if (header != null) { + if (header.length() == 0) { + throw new IllegalArgumentException("A header cannot be an empty string."); + } + + String[] clauseStrings = parseDelimitedString(header, PATH_SEPARATOR); + + List<HeaderClause> completeList = new ArrayList<HeaderClause>(); + for (int i = 0; (clauseStrings != null) && (i < clauseStrings.length); i++) { + completeList.add(parseClause(clauseStrings[i])); + } + + return completeList; + } + + return null; + + } + + // Like this: path; path; dir1:=dirval1; dir2:=dirval2; attr1=attrval1; attr2=attrval2 + private static HeaderClause parseClause(String clauseString) throws IllegalArgumentException { + // Break string into semi-colon delimited pieces. + String[] pieces = parseDelimitedString(clauseString, SEGMENT_SEPARATOR); + + // Count the number of different paths; paths + // will not have an '=' in their string. This assumes + // that paths come first, before directives and + // attributes. + int pathCount = 0; + for (int pieceIdx = 0; pieceIdx < pieces.length; pieceIdx++) { + if (pieces[pieceIdx].indexOf('=') >= 0) { + break; + } + pathCount++; + } + + // Create an array of paths. + String[] paths = new String[pathCount]; + System.arraycopy(pieces, 0, paths, 0, pathCount); + + // Parse the directives/attributes. + Map<String, String> dirsMap = new HashMap<String, String>(); + Map<String, String> attrsMap = new HashMap<String, String>(); + int idx = -1; + String sep = null; + for (int pieceIdx = pathCount; pieceIdx < pieces.length; pieceIdx++) { + // Check if it is a directive. + if ((idx = pieces[pieceIdx].indexOf(DIRECTIVE_SEPARATOR)) >= 0) { + sep = DIRECTIVE_SEPARATOR; + } + // Check if it is an attribute. + else if ((idx = pieces[pieceIdx].indexOf(ATTRIBUTE_SEPARATOR)) >= 0) { + sep = ATTRIBUTE_SEPARATOR; + } + // It is an error. + else { + throw new IllegalArgumentException("Not a directive/attribute: " + clauseString); + } + + String key = pieces[pieceIdx].substring(0, idx).trim(); + String value = pieces[pieceIdx].substring(idx + sep.length()).trim(); + + // Remove quotes, if value is quoted. + if (value.startsWith(QUOTE) && value.endsWith(QUOTE)) { + value = value.substring(1, value.length() - 1); + } + + // Save the directive/attribute in the appropriate array. + if (sep.equals(DIRECTIVE_SEPARATOR)) { + // Check for duplicates. + if (dirsMap.get(key) != null) { + throw new IllegalArgumentException("Duplicate directive: " + key); + } + dirsMap.put(key, value); + } else { + // Check for duplicates. + if (attrsMap.get(key) != null) { + throw new IllegalArgumentException("Duplicate attribute: " + key); + } + attrsMap.put(key, value); + } + } + + StringBuffer path = new StringBuffer(); + for (int i = 0; i < paths.length; i++) { + path.append(paths[i]); + if (i != paths.length - 1) { + path.append(';'); + } + } + + HeaderClause descriptor = new HeaderClause(); + descriptor.text = clauseString; + descriptor.value = path.toString(); + descriptor.valueComponents = paths; + descriptor.attributes = attrsMap; + descriptor.directives = dirsMap; + + return descriptor; + } + + /** + * Parses delimited string and returns an array containing the tokens. This + * parser obeys quotes, so the delimiter character will be ignored if it is + * inside of a quote. This method assumes that the quote character is not + * included in the set of delimiter characters. + * @param value the delimited string to parse. + * @param delim the characters delimiting the tokens. + * @return an array of string tokens or null if there were no tokens. + **/ + private static String[] parseDelimitedString(String value, String delim) { + if (value == null) { + value = ""; + } + + List<String> list = new ArrayList<String>(); + + int CHAR = 1; + int DELIMITER = 2; + int STARTQUOTE = 4; + int ENDQUOTE = 8; + + StringBuffer sb = new StringBuffer(); + + int expecting = (CHAR | DELIMITER | STARTQUOTE); + + for (int i = 0; i < value.length(); i++) { + char c = value.charAt(i); + + boolean isDelimiter = (delim.indexOf(c) >= 0); + boolean isQuote = (c == QUOTE_CHAR); + + if (isDelimiter && ((expecting & DELIMITER) > 0)) { + list.add(sb.toString().trim()); + sb.delete(0, sb.length()); + expecting = (CHAR | DELIMITER | STARTQUOTE); + } else if (isQuote && ((expecting & STARTQUOTE) > 0)) { + sb.append(c); + expecting = CHAR | ENDQUOTE; + } else if (isQuote && ((expecting & ENDQUOTE) > 0)) { + sb.append(c); + expecting = (CHAR | STARTQUOTE | DELIMITER); + } else if ((expecting & CHAR) > 0) { + sb.append(c); + } else { + throw new IllegalArgumentException("Invalid delimited string: " + value); + } + } + + if (sb.length() > 0) { + list.add(sb.toString().trim()); + } + + return (String[])list.toArray(new String[list.size()]); + } + + public static class HeaderClause { + private String text; + private String value; + private String[] valueComponents; + private Map<String, String> attributes; + private Map<String, String> directives; + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String[] getValueComponents() { + return valueComponents; + } + + public void setValueComponents(String[] valueComponents) { + this.valueComponents = valueComponents; + } + + public Map<String, String> getAttributes() { + return attributes; + } + + public Map<String, String> getDirectives() { + return directives; + } + + public String toString() { + String text = null; + if (text == null) { + StringBuffer buf = new StringBuffer(); + if (value == null) { + int start = buf.length(); + for (int i = 0; i < valueComponents.length; i++) { + if (i != valueComponents.length - 1) { + buf.append(valueComponents[i]).append(';'); + } else { + buf.append(valueComponents[i]); + } + } + int end = buf.length(); + if (end > start) { + value = buf.substring(start, end); + } + } + buf.append(value); + for (Map.Entry<String, String> e : attributes.entrySet()) { + buf.append(';').append(e.getKey()).append("=\"").append(e.getValue()).append("\""); + } + for (Map.Entry<String, String> e : directives.entrySet()) { + buf.append(';').append(e.getKey()).append(":=\"").append(e.getValue()).append("\""); + } + text = buf.toString(); + } + return text; + } + + } + + /** + * Returns a QName object from a QName expressed as {ns}name + * or ns#name. + * + * @param qname + * @return + */ + public static QName getQName(String qname) { + if (qname == null) { + return null; + } + qname = qname.trim(); + if (qname.startsWith("{")) { + int h = qname.indexOf('}'); + if (h != -1) { + return new QName(qname.substring(1, h), qname.substring(h + 1)); + } + } else { + int h = qname.indexOf('#'); + if (h != -1) { + return new QName(qname.substring(0, h), qname.substring(h + 1)); + } + } + return new QName(qname); + } + + public static String toHeader(List<HeaderClause> descriptors) { + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < descriptors.size(); i++) { + HeaderClause descriptor = descriptors.get(i); + buf.append(descriptor); + if (i != descriptors.size() - 1) { + buf.append(','); + } + } + return buf.toString(); + } + + public static String merge(String... headers) { + List<HeaderClause> merged = new ArrayList<HeaderClause>(); + for (String header : headers) { + if (header == null || header.length() == 0) { + continue; + } + List<HeaderClause> descriptors = parse(header); + merged.addAll(descriptors); + } + Set<String> values = new HashSet<String>(); + for (Iterator<HeaderClause> i = merged.iterator(); i.hasNext();) { + if (!values.add(i.next().getValue())) { + i.remove(); + } + } + return toHeader(merged); + } +} diff --git a/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/ModuleBundlesBuildMojo.java b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/ModuleBundlesBuildMojo.java new file mode 100644 index 0000000000..12733181e8 --- /dev/null +++ b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/ModuleBundlesBuildMojo.java @@ -0,0 +1,1338 @@ +/* + * 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-<version>.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 = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; + private static final String ASL_HEADER = + "<!--" + "\n * Licensed to the Apache Software Foundation (ASF) under one" + + "\n * or more contributor license agreements. See the NOTICE file" + + "\n * distributed with this work for additional information" + + "\n * regarding copyright ownership. The ASF licenses this file" + + "\n * to you under the Apache License, Version 2.0 (the" + + "\n * \"License\"); you may not use this file except in compliance" + + "\n * with the License. You may obtain a copy of the License at" + + "\n * " + + "\n * http://www.apache.org/licenses/LICENSE-2.0" + + "\n * " + + "\n * Unless required by applicable law or agreed to in writing," + + "\n * software distributed under the License is distributed on an" + + "\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY" + + "\n * KIND, either express or implied. See the License for the" + + "\n * specific language governing permissions and limitations" + + "\n * under the License." + + "\n-->"; + + /** + * Group the artifacts by distribution poms + */ + private class ProjectSet { + // Distribution projects + private Map<String, MavenProject> projects; + // Key: the pom artifact id + // Value: the names for the artifacts + private Map<String, Set<String>> nameMap = new HashMap<String, Set<String>>(); + + private Map<String, String> artifactToNameMap = new HashMap<String, String>(); + + public ProjectSet(List<MavenProject> projects) { + super(); + this.projects = new HashMap<String, MavenProject>(); + 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<String> names = nameMap.get(p.getArtifactId()); + if (names == null) { + names = new HashSet<String>(); + 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<File> jarFiles = new HashSet<File>(); + 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<Artifact> 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<String> excludedFileNames = new HashSet<String>(); + if (excludeDirectories != null) { + for (File f : excludeDirectories) { + if (f.isDirectory()) { + for (String n : f.list()) { + excludedFileNames.add(n); + } + } + } + } + Set<String> includedGroupIds = new HashSet<String>(); + if (includeGroupIds != null) { + for (String g : includeGroupIds) { + includedGroupIds.add(g); + } + } + Set<String> excludedGroupIds = new HashSet<String>(); + if (excludeGroupIds != null) { + for (String g : excludeGroupIds) { + excludedGroupIds.add(g); + } + } + + // Find all the distribution poms + List<MavenProject> poms = new ArrayList<MavenProject>(); + 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<String> typeSet = new HashSet<String>(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<File> jarFiles = new HashSet<File>(); + 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<File> jarFiles = new HashSet<File>(); + 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<Artifact> getDependencyArtifacts(MavenProject project) throws DependencyTreeBuilderException, + ArtifactResolutionException, ArtifactNotFoundException { + Log log = getLog(); + Set<Artifact> artifacts = new HashSet<Artifact>(); + 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<String>(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<String, Set<String>> e : jarNames.nameMap.entrySet()) { + Set<String> 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("<project name=\"tuscany." + name + "\">"); + ps.println(" <property name=\"tuscany.distro\" value=\"" + name + "\"/>"); + ps.println(" <property name=\"tuscany.manifest\" value=\"" + new File(feature, manifestJarName) + .getCanonicalPath() + + "\"/>"); + ps.println(" <path id=\"" + "tuscany.path" + "\">"); + ps.println(" <fileset dir=\"" + root.getCanonicalPath() + "\">"); + for (String jar : jars) { + ps.println(" <include name=\"" + jar + "\"/>"); + } + ps.println(" </fileset>"); + ps.println(" </path>"); + ps.println("</project>"); + } + } + + private void generateManifestJar(ProjectSet jarNames, File root, Log log) throws FileNotFoundException, IOException { + for (Map.Entry<String, Set<String>> e : jarNames.nameMap.entrySet()) { + MavenProject pom = jarNames.getProject(e.getKey()); + Set<String> 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<String, Set<String>> e : bundleLocations.nameMap.entrySet()) { + Set<String> locations = new HashSet<String>(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<String, Set<String>> e : bundleLocations.nameMap.entrySet()) { + Set<String> locations = new HashSet<String>(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<String, Set<String>> e : bundleSymbolicNames.nameMap.entrySet()) { + Set<String> bundles = new HashSet<String>(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<String> ids, String[] features) { + ps.println(XML_PI); + ps.println("<?pde version=\"3.2\"?>"); + ps.println(ASL_HEADER); + + ps.println("<target name=\"Eclipse Target - " + pom + "\">"); + + if (executionEnvironment != null) { + ps.println(" <targetJRE>"); + ps.println(" <execEnv>" + executionEnvironment + "</execEnv>"); + ps.println(" </targetJRE>"); + } + + if (useDefaultLocation) { + ps.println(" <location useDefault=\"true\"/>"); + } else { + ps.println(" <location path=\"" + targetDirectory + "\"/>"); + } + + // ps.println("<content useAllPlugins=\"true\">"); + ps.println(" <content>"); + ps.println(" <plugins>"); + for (String id : ids) { + ps.println(" <plugin id=\"" + id + "\"/>"); + } + ps.println(" </plugins>"); + ps.println(" <features>"); + if (features != null) { + for (String f : features) { + ps.println(" <feature id=\"" + f + "\"/>"); + } + } + ps.println(" </features>"); + if (useDefaultLocation) { + ps.println(" <extraLocations>"); + // Not sure why the extra path needs to the plugins folder + ps.println(" <location path=\"" + targetDirectory + "\"/>"); + ps.println(" </extraLocations>"); + } + ps.println(" </content>"); + + ps.println("</target>"); + + } + + private void writePDE35Target(PrintStream ps, String pom, Set<String> ids, String[] features) { + ps.println(XML_PI); + ps.println("<?pde version=\"3.5\"?>"); + ps.println(ASL_HEADER); + + ps.println("<target name=\"Eclipse PDE 3.5 Target - " + pom + "\">"); + + if (executionEnvironment != null) { + ps + .println(" <targetJRE path=\"" + "org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/" + + executionEnvironment + + "\"/>"); + } + + ps.println("<locations>"); + if (ids.size() > 0) { + ps.println(" <location path=\"" + targetDirectory + "\" type=\"Directory\">"); + ps.println(" <includeBundles>"); + for (String id : ids) { + ps.println(" <plugin id=\"" + id + "\"/>"); + } + ps.println(" </includeBundles>"); + ps.println(" </location>"); + } + + /* + if (useDefaultLocation) { + ps.println(" <location path=\"${eclipse_home}\" type=\"Profile\"/>"); + } + */ + + /* + if (features != null) { + for (String f : features) { + ps.println(" <location id=\"" + f + "\" path=\"\" type=\"Feature\"/>"); + } + } + */ + + ps.println("</locations>"); + ps.println("</target>"); + + } + + private static void writePluginXML(PrintStream ps) { + ps.println(XML_PI); + ps.println("<?pde version=\"3.2\"?>"); + ps.println(ASL_HEADER); + ps.println("<plugin>"); + ps.println("<extension point = \"org.eclipse.pde.core.targets\">"); + ps.println("<target"); + ps.println("id=\"org.apache.tuscany.sca.target\""); + ps.println("name=\"Apache Tuscany Eclipse Target\""); + ps.println("path=\"tuscany.target\"/>"); + ps.println("</extension>"); + ps.println("</plugin>"); + } +} diff --git a/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/OSGIArtifactVersion.java b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/OSGIArtifactVersion.java new file mode 100644 index 0000000000..d87ae0cb9f --- /dev/null +++ b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/OSGIArtifactVersion.java @@ -0,0 +1,219 @@ +/* + * 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 java.util.StringTokenizer; + +import org.apache.maven.artifact.versioning.ArtifactVersion; + +public class OSGIArtifactVersion implements ArtifactVersion { + private Integer buildNumber; + + private Integer incrementalVersion; + + private Integer majorVersion; + + private Integer minorVersion; + + private String qualifier; + + private String unparsed; + + public OSGIArtifactVersion(String version) { + parseVersion(version); + } + + public int compareTo(Object o) { + ArtifactVersion otherVersion = (ArtifactVersion)o; + + int result = getMajorVersion() - otherVersion.getMajorVersion(); + if (result == 0) { + result = getMinorVersion() - otherVersion.getMinorVersion(); + } + if (result == 0) { + result = getIncrementalVersion() - otherVersion.getIncrementalVersion(); + } + if (result == 0) { + if (this.qualifier != null) { + String otherQualifier = otherVersion.getQualifier(); + + if (otherQualifier != null) { + if ((this.qualifier.length() > otherQualifier.length()) && this.qualifier + .startsWith(otherQualifier)) { + // here, the longer one that otherwise match is + // considered older + result = -1; + } else if ((this.qualifier.length() < otherQualifier.length()) && otherQualifier + .startsWith(this.qualifier)) { + // here, the longer one that otherwise match is + // considered older + result = 1; + } else { + result = this.qualifier.compareTo(otherQualifier); + } + } else { + // otherVersion has no qualifier but we do - that's newer + result = -1; + } + } else if (otherVersion.getQualifier() != null) { + // otherVersion has a qualifier but we don't, we're newer + result = 1; + } else { + result = getBuildNumber() - otherVersion.getBuildNumber(); + } + } + return result; + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + + if (false == (other instanceof ArtifactVersion)) { + return false; + } + + return 0 == compareTo(other); + } + + public int getBuildNumber() { + return this.buildNumber != null ? this.buildNumber.intValue() : 0; + } + + public int getIncrementalVersion() { + return this.incrementalVersion != null ? this.incrementalVersion.intValue() : 0; + } + + public int getMajorVersion() { + return this.majorVersion != null ? this.majorVersion.intValue() : 0; + } + + public int getMinorVersion() { + return this.minorVersion != null ? this.minorVersion.intValue() : 0; + } + + public String getQualifier() { + return this.qualifier; + } + + @Override + public int hashCode() { + int result = 1229; + + result = 1223 * result + getMajorVersion(); + result = 1223 * result + getMinorVersion(); + result = 1223 * result + getIncrementalVersion(); + result = 1223 * result + getBuildNumber(); + + if (null != getQualifier()) { + result = 1223 * result + getQualifier().hashCode(); + } + + return result; + } + + public final void parseVersion(String version) { + this.unparsed = version; + + int index = version.indexOf("-"); + + String part1; + String part2 = null; + + if (index < 0) { + part1 = version; + } else { + part1 = version.substring(0, index); + part2 = version.substring(index + 1); + } + + if (part2 != null) { + try { + if ((part2.length() == 1) || !part2.startsWith("0")) { + this.buildNumber = Integer.valueOf(part2); + } else { + this.qualifier = part2; + } + } catch (NumberFormatException e) { + this.qualifier = part2; + } + } + + if ((part1.indexOf(".") < 0) && !part1.startsWith("0")) { + try { + this.majorVersion = Integer.valueOf(part1); + } catch (NumberFormatException e) { + // qualifier is the whole version, including "-" + this.qualifier = version; + this.buildNumber = null; + } + } else { + StringTokenizer tok = new StringTokenizer(part1, "."); + + String s; + + if (tok.hasMoreTokens()) { + s = tok.nextToken(); + try { + this.majorVersion = Integer.valueOf(s); + + if (tok.hasMoreTokens()) { + s = tok.nextToken(); + try { + this.minorVersion = Integer.valueOf(s); + if (tok.hasMoreTokens()) { + + s = tok.nextToken(); + try { + this.incrementalVersion = Integer.valueOf(s); + + } catch (NumberFormatException e) { + this.qualifier = s; + } + } + } catch (NumberFormatException e) { + this.qualifier = s; + } + } + } catch (NumberFormatException e) { + this.qualifier = s; + } + } + + if (tok.hasMoreTokens()) { + StringBuffer qualifier = new StringBuffer(this.qualifier != null ? this.qualifier : ""); + qualifier.append(tok.nextToken()); + while (tok.hasMoreTokens()) { + qualifier.append("_"); + qualifier.append(tok.nextToken()); + } + + this.qualifier = qualifier.toString(); + } + + } + } + + @Override + public String toString() { + return this.unparsed; + } +} diff --git a/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/ThirdPartyBundleBuildMojo.java b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/ThirdPartyBundleBuildMojo.java new file mode 100644 index 0000000000..880f1379e0 --- /dev/null +++ b/maven-plugins/tags/maven-bundle-plugin-1.0.7-RC1/src/main/java/org/apache/tuscany/maven/bundle/plugin/ThirdPartyBundleBuildMojo.java @@ -0,0 +1,152 @@ +/* + * 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 java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; +import java.util.jar.Manifest; + +import org.apache.maven.artifact.Artifact; +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; + +/** + * A Maven plugin that builds an OSGi bundle for the project's third-party dependencies. + * + * @version $Rev$ $Date$ + * @goal assemble-thirdparty-bundle + * @phase generate-resources + * @requiresDependencyResolution test + * @description Build an OSGi bundle for the project's third party dependencies + */ +public class ThirdPartyBundleBuildMojo extends AbstractMojo { + + /** + * The project to build the bundle for. + * + * @parameter expression="${project}" + * @required + * @readonly + */ + private MavenProject project; + + /** + * The bundle symbolic name + * + * @parameter + */ + private String symbolicName; + + public void execute() throws MojoExecutionException { + Log log = getLog(); + + String projectGroupId = project.getGroupId(); + Set<File> jarFiles = new HashSet<File>(); + for (Object o : project.getArtifacts()) { + Artifact artifact = (Artifact)o; + + if (!(Artifact.SCOPE_COMPILE.equals(artifact.getScope()) || Artifact.SCOPE_RUNTIME.equals(artifact + .getScope()))) { + if (log.isDebugEnabled()) { + log.debug("Skipping artifact: " + artifact); + } + continue; + } + if (!"jar".equals(artifact.getType())) { + continue; + } + if (projectGroupId.equals(artifact.getGroupId())) { + continue; + } + + if (log.isDebugEnabled()) { + log.debug("Artifact: " + artifact); + } + String bundleName = null; + try { + bundleName = BundleUtil.getBundleSymbolicName(artifact.getFile()); + } catch (IOException e) { + throw new MojoExecutionException(e.getMessage(), e); + } + if (bundleName == null || true) { + if (artifact.getFile().exists()) { + log.info("Adding third party jar: " + artifact); + jarFiles.add(artifact.getFile()); + } else { + log.warn("Third party jar not found: " + artifact); + } + } + } + + try { + String version = BundleUtil.osgiVersion(project.getVersion()); + + Manifest mf = BundleUtil.libraryManifest(jarFiles, project.getName(), symbolicName, version, "lib"); + File file = new File(project.getBasedir(), "META-INF"); + file.mkdir(); + file = new File(file, "MANIFEST.MF"); + if (log.isDebugEnabled()) { + log.debug("Generating " + file); + } + + FileOutputStream fos = new FileOutputStream(file); + write(mf, fos); + fos.close(); + + File lib = new File(project.getBasedir(), "lib"); + if (lib.isDirectory()) { + for (File c : lib.listFiles()) { + c.delete(); + } + } + lib.mkdir(); + byte[] buf = new byte[4096]; + for (File jar : jarFiles) { + File jarFile = new File(lib, jar.getName()); + if (log.isDebugEnabled()) { + log.debug("Copying " + jar + " to " + jarFile); + } + 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(); + } + } catch (Exception e) { + throw new MojoExecutionException(e.getMessage(), e); + } + + } + +} |