You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ri...@apache.org on 2006/05/24 09:42:31 UTC
svn commit: r409086 - in
/incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin:
Clazz.java Jar.java OsgiJarMojo.java QuotedTokenizer.java
Author: rickhall
Date: Wed May 24 00:42:30 2006
New Revision: 409086
URL: http://svn.apache.org/viewvc?rev=409086&view=rev
Log:
Updating the maven OSGi plugin to incorporate Peter Kriens' modifications
for import-package header generation and more. I built the project and
everything seemed to work fine. (FELIX-74)
Added:
incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/Clazz.java (with props)
incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/Jar.java (with props)
incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/QuotedTokenizer.java (with props)
Modified:
incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/OsgiJarMojo.java
Added: incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/Clazz.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/Clazz.java?rev=409086&view=auto
==============================================================================
--- incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/Clazz.java (added)
+++ incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/Clazz.java Wed May 24 00:42:30 2006
@@ -0,0 +1,190 @@
+package org.apache.felix.tools.maven.plugin;
+
+import java.io.*;
+import java.util.*;
+
+public class Clazz {
+ static byte SkipTable[] = {0, // 0 non existent
+ -1, // 1 CONSTANT_utf8 UTF 8, handled in
+ // method
+ -1, // 2
+ 4, // 3 CONSTANT_Integer
+ 4, // 4 CONSTANT_Float
+ 8, // 5 CONSTANT_Long (index +=2!)
+ 8, // 6 CONSTANT_Double (index +=2!)
+ -1, // 7 CONSTANT_Class
+ 2, // 8 CONSTANT_String
+ 4, // 9 CONSTANT_FieldRef
+ 4, // 10 CONSTANT_MethodRef
+ 4, // 11 CONSTANT_InterfaceMethodRef
+ 4, // 12 CONSTANT_NameAndType
+ };
+
+ Set imports = new HashSet();
+ String path;
+ Jar jar;
+
+ public Clazz(Jar jar, String path, InputStream in) throws IOException {
+ this.path = path;
+ this.jar = jar;
+ DataInputStream din = new DataInputStream(in);
+ parseClassFile(din);
+ }
+
+
+ void parseClassFile(DataInputStream in)
+ throws IOException {
+ Set classes = new HashSet();
+ Set descriptors = new HashSet();
+ Hashtable pool = new Hashtable();
+ try {
+ int magic = in.readInt();
+ if (magic != 0xCAFEBABE)
+ throw new IOException(
+ "Not a valid class file (no CAFEBABE header)");
+ in.readShort(); // minor version
+ in.readShort(); // major version
+ int count = in.readUnsignedShort();
+ process: for (int i = 1; i < count; i++) {
+ byte tag = in.readByte();
+ switch (tag) {
+ case 0 :
+ break process;
+ case 1 :
+ // CONSTANT_Utf8
+ String name = in.readUTF();
+ pool.put(new Integer(i), name);
+ break;
+ // A Class constant is just a short reference in
+ // the constant pool
+ case 7 :
+ // CONSTANT_Class
+ Integer index = new Integer(in.readShort());
+ classes.add(index);
+ break;
+ // For some insane optimization reason are
+ // the long and the double two entries in the
+ // constant pool. See 4.4.5
+ case 5 :
+ // CONSTANT_Long
+ case 6 :
+ // CONSTANT_Double
+ in.skipBytes(8);
+ i++;
+ break;
+
+ // Interface Method Ref
+ case 12 :
+ in.readShort(); // Name index
+ int descriptorIndex = in.readShort();
+ descriptors.add(new Integer(descriptorIndex));
+ break;
+
+ // We get the skip count for each record type
+ // from the SkipTable. This will also automatically
+ // abort when
+ default :
+ if (tag == 2)
+ throw new IOException("Invalid tag " + tag);
+ in.skipBytes(SkipTable[tag]);
+ break;
+ }
+ }
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ return;
+ }
+ //
+ // Now iterate over all classes we found and
+ // parse those as well. We skip duplicates
+ //
+
+ for (Iterator e = classes.iterator(); e.hasNext();) {
+ Integer n = (Integer) e.next();
+ String next = (String) pool.get(n);
+ if (next != null) {
+ String normalized = normalize(next);
+ if (normalized != null) {
+ // For purposes of trying to guess the activator class, we assume
+ // that any class that references the BundleActivator interface
+ // must be a BundleActivator implementation.
+ if ( normalized.startsWith("org/osgi/framework/BundleActivator")) {
+ String cname = path.replace('/', '.');
+ cname = cname.substring(0,cname.length()-".class" .length());
+ jar.addActivator(cname);
+ }
+ String pack = getPackage(normalized);
+ if (!pack.startsWith("java."))
+ imports.add(pack);
+ }
+ }
+ else
+ throw new IllegalArgumentException("Invalid class, parent=");
+ }
+ for (Iterator e = descriptors.iterator(); e.hasNext();) {
+ Integer n = (Integer) e.next();
+ String prototype = (String) pool.get(n);
+ if (prototype != null)
+ parseDescriptor(prototype);
+ }
+ }
+
+ void parseDescriptor(String prototype) {
+ addReference(prototype);
+ StringTokenizer st = new StringTokenizer(prototype, "(;)", true);
+ while (st.hasMoreTokens()) {
+ if (st.nextToken().equals("(")) {
+ String token = st.nextToken();
+ while (!token.equals(")")) {
+ addReference(token);
+ token = st.nextToken();
+ }
+ token = st.nextToken();
+ addReference(token);
+ }
+ }
+ }
+
+ private void addReference(String token) {
+ if (token.startsWith("L")) {
+ String clazz = normalize(token.substring(1));
+ if (clazz.startsWith("java/"))
+ return;
+ String pack = getPackage(clazz);
+ imports.add(pack);
+ }
+ }
+
+ static String normalize(String s) {
+ if (s.startsWith("[L"))
+ return normalize(s.substring(2));
+ if (s.startsWith("["))
+ if (s.length() == 2)
+ return null;
+ else
+ return normalize(s.substring(1));
+ if (s.endsWith(";"))
+ return normalize(s.substring(0, s.length() - 1));
+ return s + ".class";
+ }
+
+ static String getPackage(String clazz) {
+ int n = clazz.lastIndexOf('/');
+ if (n < 0)
+ return ".";
+ return clazz.substring(0, n).replace('/', '.');
+ }
+
+
+ public Collection getReferred() {
+ return imports;
+ }
+
+
+ public Object getPath() {
+ return path;
+ }
+
+
+}
Propchange: incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/Clazz.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/Jar.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/Jar.java?rev=409086&view=auto
==============================================================================
--- incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/Jar.java (added)
+++ incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/Jar.java Wed May 24 00:42:30 2006
@@ -0,0 +1,176 @@
+package org.apache.felix.tools.maven.plugin;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+import java.util.zip.*;
+
+public class Jar {
+ Map resources = new TreeMap();
+ Map imports = new HashMap();
+ Map exports = new HashMap();
+ Manifest manifest;
+ boolean manifestFirst;
+ String name;
+ Jar parent;
+ List activators = new ArrayList();
+
+ public Jar(Jar parent, String name, InputStream in) throws IOException {
+ this.name = name;
+ this.parent = parent;
+ ZipInputStream jar = new ZipInputStream(in);
+ ZipEntry entry = jar.getNextEntry();
+ boolean first = true;
+ while (entry != null) {
+ String path = entry.getName();
+
+ if (path.endsWith(".class")) {
+ Clazz clazz = new Clazz(this, path, jar);
+ resources.put(path, clazz);
+ }
+ else if (path.endsWith(".jar")) {
+ Jar pool = new Jar(this, path, jar);
+ resources.put(path, pool);
+ }
+ else if (path.endsWith("/packageinfo")
+ && !path.startsWith("OSGI-OPT")) {
+ String version = parsePackageInfo(jar, exports);
+ resources.put(path, version);
+ }
+ else if (path.equals("META-INF/MANIFEST.MF")) {
+ manifestFirst = first;
+ manifest = new Manifest(jar);
+ }
+ else
+ resources.put(path, null);
+
+ entry = jar.getNextEntry();
+ if (!path.endsWith("/"))
+ first = false;
+ }
+ }
+
+ public Jar(Jar parent, String name, File rootDir) throws IOException {
+ this.name = name;
+ this.parent = parent;
+ traverse(rootDir.getAbsolutePath().length(), rootDir, rootDir.list());
+ }
+
+ void traverse(int root, File dir, String list[]) throws IOException {
+ for (int i = 0; i < list.length; i++) {
+ File sub = new File(dir, list[i]);
+ if (sub.isDirectory())
+ traverse(root, sub, sub.list());
+ else {
+ String path = sub.getAbsolutePath().substring(root + 1)
+ .replace(File.separatorChar, '/');
+ FileInputStream in = new FileInputStream(sub);
+
+ if (path.endsWith(".class")) {
+ Clazz clazz = new Clazz(this, path, in);
+ resources.put(path, clazz);
+ }
+ else if (path.endsWith(".jar")) {
+ Jar pool = new Jar(this, path, in);
+ resources.put(path, pool);
+ }
+ else if (path.endsWith("/packageinfo")
+ && !path.startsWith("OSGI-OPT")) {
+ String version = parsePackageInfo(in, exports);
+ resources.put(path, version);
+ }
+ else if (path.endsWith("META-INF/MANIFEST.MF")) {
+ manifest = new Manifest(in);
+ }
+ else
+ resources.put(path, null);
+ }
+ }
+ }
+
+ private static String parsePackageInfo(InputStream jar, Map exports)
+ throws IOException {
+ try {
+ byte[] buf = collect(jar, 0);
+ String line = new String(buf);
+ StringTokenizer qt = new StringTokenizer(line, " \r\n\t");
+ if (qt.hasMoreElements()) {
+ qt.nextToken();
+ if (qt.hasMoreElements()) {
+ String version = qt.nextToken();
+ return version;
+ }
+ }
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * Convenience method to turn an inputstream into a byte array. The method
+ * uses a recursive algorithm to minimize memory usage.
+ *
+ * @param in stream with data
+ * @param offset where we are in the stream
+ * @returns byte array filled with data
+ */
+ private static byte[] collect(InputStream in, int offset)
+ throws IOException {
+ byte[] result;
+ byte[] buffer = new byte[10000];
+ int size = in.read(buffer);
+ if (size <= 0)
+ return new byte[offset];
+ else
+ result = collect(in, offset + size);
+ System.arraycopy(buffer, 0, result, offset, size);
+ return result;
+ }
+
+ public Manifest getManifest() {
+ return manifest;
+ }
+
+ public Object getEntry(String resource) {
+ return resources.get(resource);
+ }
+
+ public boolean exists(String jarOrDir) {
+ return resources.keySet().contains(jarOrDir);
+ }
+
+ public Set getEntryPaths(String prefix) {
+ Set result = new TreeSet();
+ for (Iterator i = resources.keySet().iterator(); i.hasNext();) {
+ String path = (String) i.next();
+ if (path.startsWith(prefix))
+ result.add(path);
+ }
+ return result;
+ }
+
+ String getName() {
+ return name;
+ }
+
+ public String toString() {
+ return getName();
+ }
+
+ public void addActivator(String path) {
+ if (parent != null)
+ parent.addActivator(path);
+ else {
+ activators.add(path);
+ }
+
+ }
+
+ public boolean containsActivator(String path) {
+ if (parent != null)
+ return parent.containsActivator(path);
+ return activators.contains(path);
+ }
+}
Propchange: incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/Jar.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/OsgiJarMojo.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/OsgiJarMojo.java?rev=409086&r1=409085&r2=409086&view=diff
==============================================================================
--- incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/OsgiJarMojo.java (original)
+++ incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/OsgiJarMojo.java Wed May 24 00:42:30 2006
@@ -17,24 +17,15 @@
package org.apache.felix.tools.maven.plugin;
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.maven.archiver.MavenArchiveConfiguration;
-import org.apache.maven.archiver.MavenArchiver;
-import org.apache.maven.artifact.Artifact;
-import org.apache.maven.artifact.DependencyResolutionRequiredException;
-import org.apache.maven.plugin.AbstractMojo;
-import org.apache.maven.plugin.MojoExecutionException;
+import java.io.*;
+import java.util.*;
+
+import org.apache.maven.archiver.*;
+import org.apache.maven.artifact.*;
+import org.apache.maven.plugin.*;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.archiver.ArchiverException;
-import org.codehaus.plexus.archiver.jar.JarArchiver;
-import org.codehaus.plexus.archiver.jar.ManifestException;
+import org.codehaus.plexus.archiver.jar.*;
import org.codehaus.plexus.util.FileUtils;
/**
@@ -47,262 +38,609 @@
* @requiresDependencyResolution runtime
* @description build an OSGi bundle jar
*/
-public class OsgiJarMojo extends AbstractMojo
-{
- private static final String[] EMPTY_STRING_ARRAY = {};
-
- /**
- * The Maven project.
- * @parameter expression="${project}"
- * @required
- * @readonly
- */
- private MavenProject project;
-
- /**
- * The directory for the generated JAR.
- *
- * @parameter expression="${project.build.directory}"
- * @required
- */
- private String buildDirectory;
-
- /**
- * The directory containing generated classes.
- *
- * @parameter expression="${project.build.outputDirectory}"
- * @required
- * @readonly
- */
- private File outputDirectory;
-
- /**
- * The name of the generated JAR file.
- *
- * @parameter alias="jarName" expression="${project.build.finalName}"
- * @required
- */
- private String jarName;
-
- /**
- * The Jar archiver.
- *
- * @parameter expression="${component.org.codehaus.plexus.archiver.Archiver#jar}"
- * @required
- */
- private JarArchiver jarArchiver;
-
- /**
- * The maven archive configuration to use.
- */
- private MavenArchiveConfiguration archiveConfig = new MavenArchiveConfiguration();
-
- /**
- * The comma separated list of tokens to include in the JAR.
- * Default is '**'.
- *
- * @parameter alias="includes"
- */
- private String jarSourceIncludes = "**";
-
- /**
- * The comma separated list of tokens to exclude from the JAR.
- *
- * @parameter alias="excludes"
- */
- private String jarSourceExcludes;
-
- /**
- * @parameter
- */
- private String manifestFile;
-
- /**
- * @parameter expression="${org.apache.felix.tools.maven.plugin.OsgiManifest}"
- */
- private OsgiManifest osgiManifest;
-
- /**
- * Execute this Mojo
- *
- * @throws MojoExecutionException
- */
- public void execute() throws MojoExecutionException
- {
- File jarFile = new File( buildDirectory, jarName + ".jar" );
-
- try
- {
- performPackaging( jarFile );
- }
- catch ( Exception e )
- {
- throw new MojoExecutionException( "Error assembling JAR bundle", e );
- }
- }
-
- /**
- * Generates the JAR bundle file.
- *
- * @param jarFile the target JAR file
- * @throws IOException
- * @throws ArchiverException
- * @throws ManifestException
- * @throws DependencyResolutionRequiredException
- */
- private void performPackaging( File jarFile ) throws IOException, ArchiverException, ManifestException,
- DependencyResolutionRequiredException, MojoExecutionException
- {
- getLog().info( "Generating JAR bundle " + jarFile.getAbsolutePath() );
-
- MavenArchiver archiver = new MavenArchiver();
-
- archiver.setArchiver( jarArchiver );
- archiver.setOutputFile( jarFile );
-
- addManifestFile();
- addManifestEntries();
-
- addBundleClasspath();
- addBundleVersion();
-
- jarArchiver.addDirectory( outputDirectory, getIncludes(), getExcludes() );
-
- archiver.createArchive( project, archiveConfig );
-
- project.getArtifact().setFile( jarFile );
- }
-
- /**
- * TODO: Decide if we accept merging of entire manifest.mf files
- * Here's a big question to make a final decision at some point: Do accept
- * merging of manifest entries located in some file somewhere in the project
- * directory? If so, do we allow both file and configuration based entries
- * to be specified simultaneously and how do we merge these?
- */
- private void addManifestFile()
- {
- if ( manifestFile != null )
- {
- File file = new File( project.getBasedir().getAbsolutePath(), manifestFile );
- getLog().info( "Manifest file: " + file.getAbsolutePath() + " will be used" );
- archiveConfig.setManifestFile( file );
- }
- else
- {
- getLog().info( "No manifest file specified. Default will be used." );
- }
- }
-
- /**
- * Look for any OSGi specified manifest entries in the maven-osgi-plugin configuration
- * section of the POM. If we find some, then add them to the target artifact's manifest.
- */
- private void addManifestEntries()
- {
- if ( osgiManifest != null && osgiManifest.getEntries().size() > 0 )
- {
- Map entries = osgiManifest.getEntries();
-
- getLog().info( "Bundle manifest will be modified with the following entries: " + entries.toString() );
- archiveConfig.addManifestEntries( entries );
- }
- else
- {
- getLog().info( "No OSGi bundle manifest entries have been specified in the POM." );
- }
- }
-
- /**
- * We are going to iterate through the POM's specified JAR dependencies. If a dependency
- * has a scope of either RUNTIME or COMPILE, then we'll JAR them up inside the
- * OSGi bundle artifact. We will then add the Bundle-Classpath manifest entry.
- */
- private void addBundleClasspath() throws MojoExecutionException
- {
- StringBuffer bundleClasspath = new StringBuffer();
- Set artifacts = project.getArtifacts();
-
- for ( Iterator it = artifacts.iterator(); it.hasNext(); )
- {
- Artifact artifact = (Artifact) it.next();
- if ( !Artifact.SCOPE_PROVIDED.equals( artifact.getScope() )
- && !Artifact.SCOPE_TEST.equals( artifact.getScope() ) )
- {
- String type = artifact.getType();
-
- if ( "jar".equals( type ) )
- {
- File depFile = artifact.getFile();
-
- try
- {
- FileUtils.copyFileToDirectory( depFile, outputDirectory );
-
- if ( bundleClasspath.length() == 0 )
- {
- bundleClasspath.append( "." );
- }
-
- bundleClasspath.append( "," + artifact.getFile().getName() );
- }
- catch ( Exception e )
- {
- String errmsg = "Error copying " + depFile.getAbsolutePath() + " to "
- + outputDirectory.getAbsolutePath();
- throw new MojoExecutionException( errmsg, e );
- }
- }
- }
- }
-
- String finalPath = bundleClasspath.toString();
-
- if ( finalPath.length() != 0 )
- {
- archiveConfig.addManifestEntry( "Bundle-Classpath", finalPath );
- }
- }
-
- /**
- * Auto-set the bundle version.
- */
- private void addBundleVersion()
- {
- // Maven uses a '-' to separate the version qualifier,
- // while OSGi uses a '.', so we need to convert to a '.'
- StringBuffer sb = new StringBuffer(project.getVersion());
- if (sb.indexOf("-") >= 0)
- {
- sb.setCharAt(sb.indexOf("-"), '.');
- }
- archiveConfig.addManifestEntry( "Bundle-Version", sb.toString() );
- }
-
- /**
- * Returns a string array of the includes to be used when assembling/copying the war.
- *
- * @return an array of tokens to include
- */
- private String[] getIncludes()
- {
- return new String[] { jarSourceIncludes };
- }
-
- /**
- * Returns a string array of the excludes to be used when assembling/copying the jar.
- *
- * @return an array of tokens to exclude
- */
- private String[] getExcludes()
- {
- List excludeList = new ArrayList( FileUtils.getDefaultExcludesAsList() );
-
- if ( jarSourceExcludes != null && !"".equals( jarSourceExcludes ) )
- {
- excludeList.add( jarSourceExcludes );
- }
+public class OsgiJarMojo extends AbstractMojo {
+ private static final String[] EMPTY_STRING_ARRAY = {};
+
+ int bundleManifestVersion = 1;
+
+ /**
+ * The Maven project.
+ *
+ * @parameter expression="${project}"
+ * @required
+ * @readonly
+ */
+ private MavenProject project;
+
+ /**
+ * The directory for the generated JAR.
+ *
+ * @parameter expression="${project.build.directory}"
+ * @required
+ */
+ private String buildDirectory;
+
+ /**
+ * The directory containing generated classes.
+ *
+ * @parameter expression="${project.build.outputDirectory}"
+ * @required
+ * @readonly
+ */
+ private File outputDirectory;
+
+ /**
+ * The name of the generated JAR file.
+ *
+ * @parameter alias="jarName" expression="${project.build.finalName}"
+ * @required
+ */
+ private String jarName;
+
+ /**
+ * The Jar archiver.
+ *
+ * @parameter expression="${component.org.codehaus.plexus.archiver.Archiver#jar}"
+ * @required
+ */
+ private JarArchiver jarArchiver;
+
+ /**
+ * The maven archive configuration to use.
+ */
+ private MavenArchiveConfiguration archiveConfig = new MavenArchiveConfiguration();
+
+ /**
+ * The comma separated list of tokens to include in the JAR. Default is
+ * '**'.
+ *
+ * @parameter alias="includes"
+ */
+ private String jarSourceIncludes = "**";
+
+ /**
+ * The comma separated list of tokens to exclude from the JAR.
+ *
+ * @parameter alias="excludes"
+ */
+ private String jarSourceExcludes;
+
+ /**
+ * @parameter
+ */
+ private String manifestFile;
+
+ /**
+ * @parameter expression="${org.apache.felix.tools.maven.plugin.OsgiManifest}"
+ */
+ private OsgiManifest osgiManifest;
+
+ /**
+ * Execute this Mojo
+ *
+ * @throws MojoExecutionException
+ */
+ public void execute() throws MojoExecutionException {
+ File jarFile = new File(buildDirectory, jarName + ".jar");
+
+ try {
+ performPackaging(jarFile);
+ }
+ catch (Exception e) {
+ throw new MojoExecutionException("Error assembling JAR bundle", e);
+ }
+ }
+
+ /**
+ * Generates the JAR bundle file.
+ *
+ * @param jarFile the target JAR file
+ * @throws IOException
+ * @throws ArchiverException
+ * @throws ManifestException
+ * @throws DependencyResolutionRequiredException
+ */
+ private void performPackaging(File jarFile) throws IOException,
+ ArchiverException, ManifestException,
+ DependencyResolutionRequiredException, MojoExecutionException {
+
+ verifyDeclaredBundleManifestVersion();
+
+ getLog().info("Generating JAR bundle " + jarFile.getAbsolutePath());
+
+ MavenArchiver archiver = new MavenArchiver();
+
+ archiver.setArchiver(jarArchiver);
+ archiver.setOutputFile(jarFile);
+
+ addManifestFile();
+ addManifestEntries();
+
+ // Add the JARs that were specified in the POM
+ // as "not" provided
+ addEmbeddedJars();
+ addBundleVersion();
+
+ jarArchiver.addDirectory(outputDirectory, getIncludes(), getExcludes());
+
+ // Parse the output directory as if it was a JAR file.
+ // This creates special entries for classes, packageinfo
+ // and embedded JAR files (which are parsed as well).
+ Jar mainJar = new Jar(null, jarName, outputDirectory);
+
+ // Calculate the Bundle Classpath from the embedded
+ // JAR files. We hardcode the bcp as ., <embedded jars>
+ // TODO we add all the found JARs to the Bcp, maybe we
+ // should look if they are needed by traversing the imports.
+ List bundleClassPath = getBundleClassPath(mainJar);
+ bundleClassPath.add(0, ".");
+ createBundleClasspathHeader(bundleClassPath);
+
+ // Calculate the exports (contained) and imports (referred)
+ // The bundleClassPath contains the JARs in the right order
+ Set contained = new HashSet(); // package name
+ Set referred = new HashSet(); // package name
+ Map uses = new HashMap(); // package name => Set of package name
+
+ // Iterate over the bundle class path and calculate the contained
+ // and referred packages as well as the uses.
+ for (Iterator i = bundleClassPath.iterator(); i.hasNext();) {
+ String path = (String) i.next();
+ Jar jar = path.equals(".") ? mainJar : (Jar) mainJar.resources
+ .get(path);
+ analyzeJar(jar, contained, referred, uses);
+ }
+
+ referred.removeAll(contained);
+
+ Map exports = parseHeader(osgiManifest.getExportPackage());
+ Map imports = parseHeader(osgiManifest.getImportPackage());
+ Map dynamicImports = parseHeader(osgiManifest.getDynamicImportPackage());
+
+ if (dynamicImports != null) {
+ // Remove any dynamic imports from the referred set.
+ referred = new HashSet(referred);
+ referred.removeAll(dynamicImports.keySet());
+ }
+
+ if (exports != null) {
+ verifyDeclaredExports(exports, contained);
+ createExportHeader(exports, uses);
+ }
+
+ // If the POM file contains an import declaration,
+ // we verify its validity. Otherwise, we generate the
+ // import package header from the referred. Exports
+ // are added to automatically imports for R4 bundles.
+ if (imports == null) {
+ createImportHeader(referred, exports == null ? new HashSet()
+ : exports.keySet());
+ }
+ else {
+ verifyDeclaredImports(referred, imports);
+ }
+
+ verifyBundleActivator(mainJar);
+
+ archiver.createArchive(project, archiveConfig);
+ project.getArtifact().setFile(jarFile);
+ }
+
+ private void verifyBundleActivator(Jar mainJar) {
+ String ba = osgiManifest.getBundleActivator();
+ if (ba == null || ba.trim().length() == 0) {
+ switch ( mainJar.activators.size() ) {
+ case 0: break;
+ case 1: archiveConfig.addManifestEntry("Bundle-Activator", mainJar.activators.get(0));
+ break;
+ default:
+ getLog().info("[OSGi] No Bundle-Activator specified and multiple found" );
+ break;
+ }
+ }
+ else {
+ if( ! mainJar.activators.contains(ba))
+ getLog().warn("[OSGi] UNABLE TO VERIFY BUNDLE ACTIVATOR: " + ba);
+ }
+ }
+
+ private void createBundleClasspathHeader(List bundleClassPath) {
+ StringBuffer sb = new StringBuffer();
+ String del = ".,";
+ for (Iterator i = bundleClassPath.iterator(); i.hasNext();) {
+ sb.append(del);
+ sb.append(i.next());
+ del = ",";
+ }
+ if (sb.length() > 0)
+ archiveConfig.addManifestEntry("Bundle-Classpath", sb.toString());
+ }
+
+ /**
+ * Iterate over the declared exports from the POM, verify that they are
+ * present, add the uses clause if necessary and finally add the manifest
+ * entry.
+ *
+ * @param contained Set of contained packages
+ * @param exports Map with the export clauses from the POM
+ * @param uses Map with use clauses
+ * @throws MojoExecutionException
+ */
+ void verifyDeclaredExports(Map exports, Set contained)
+ throws MojoExecutionException {
+ Set declaredExports = exports.keySet();
+ for (Iterator i = declaredExports.iterator(); i.hasNext();) {
+ String pack = (String) i.next();
+ if (!contained.contains(pack)) {
+ getLog()
+ .error("[OSGi] EXPORTED PACKAGE NOT IN BUNDLE: " + pack);
+ throw new MojoExecutionException(
+ "Exported Package not found in bundle or JARs on bundle class path "
+ + pack);
+ }
+
+ }
+ }
+
+ /**
+ * Print out the export headers after adding the uses clause.
+ *
+ * @param exports
+ * @param uses
+ * @throws MojoExecutionException
+ */
+ void createExportHeader(Map exports, Map uses)
+ throws MojoExecutionException {
+ if (exports.size() > 0) {
+ Set declaredExports = exports.keySet();
+ for (Iterator i = declaredExports.iterator(); i.hasNext();) {
+ String pack = (String) i.next();
+ Map clause = (Map) exports.get(pack);
+
+ if (bundleManifestVersion >= 2) {
+ Set t = (Set) uses.get(pack);
+ if (t != null && !t.isEmpty()) {
+ StringBuffer sb = new StringBuffer();
+ String del = "\"";
+ for (Iterator u = t.iterator(); u.hasNext();) {
+ String usedPackage = (String) u.next();
+ if (!usedPackage.equals(pack)) {
+ sb.append(del);
+ sb.append(usedPackage);
+ del = ",";
+ }
+ }
+ sb.append("\"");
+ clause.put("uses:", sb.toString());
+ }
+ }
+ }
+ archiveConfig.addManifestEntry(
+ "Export-Package",
+ printClauses(exports));
+ }
+ }
+
+ /**
+ * Verify that the declared imports match the referred packages.
+ *
+ * @param referred referred package
+ * @param imports imported packages from POM
+ * @throws MojoExecutionException
+ */
+ void verifyDeclaredImports(Set referred, Map imports)
+ throws MojoExecutionException {
+ Set declaredImports = imports.keySet();
+ Set test = new HashSet(referred);
+ test.removeAll(declaredImports);
+ for (Iterator m = test.iterator(); m.hasNext();) {
+ Object o = m.next();
+ getLog().error("[OSGi] MISSING IMPORT: " + o);
+ throw new MojoExecutionException("Missing Import " + o);
+ }
+
+ test = new HashSet(declaredImports);
+ test.removeAll(referred);
+ for (Iterator m = test.iterator(); m.hasNext();) {
+ getLog().warn("[OSGi] SUPERFLUOUS IMPORT: " + m.next());
+ getLog()
+ .warn(
+ "[OSGi] Removing the POM Import-Package element will automatically generate the import clauses");
+ }
+ }
+
+ /**
+ * Standard OSGi header parser. This parser can handle the format clauses
+ * ::= clause ( ',' clause ) + clause ::= name ( ';' name ) (';' key '='
+ * value )
+ *
+ * This is mapped to a Map { name => Map { attr|directive => value } }
+ *
+ * @param value
+ * @return
+ * @throws MojoExecutionException
+ */
+ static Map parseHeader(String value) throws MojoExecutionException {
+ if (value == null || value.trim().length() == 0)
+ return null;
+
+ Map result = new HashMap();
+ QuotedTokenizer qt = new QuotedTokenizer(value, ";=,");
+ char del;
+ do {
+ boolean hadAttribute = false;
+ Map clause = new HashMap();
+ List aliases = new ArrayList();
+ aliases.add(qt.nextToken());
+ del = qt.getSeparator();
+ while (del == ';') {
+ String adname = qt.nextToken();
+ if (qt.getSeparator() != '=') {
+ if (hadAttribute)
+ throw new MojoExecutionException(
+ "Header contains name field after attribute or directive: "
+ + adname + " from " + value);
+ aliases.add(adname);
+ }
+ else {
+ String advalue = qt.nextToken();
+ clause.put(adname, advalue);
+ del = qt.getSeparator();
+ hadAttribute = true;
+ }
+ }
+ for (Iterator i = aliases.iterator(); i.hasNext();) {
+ result.put(i.next(), clause);
+ }
+ } while (del == ',');
+ return result;
+ }
+
+ /**
+ * Create the import header, taking into account R4 automatic import clauses
+ * for the exports.
+ *
+ * @param referred
+ * @param contained
+ */
+ void createImportHeader(Set referred, Set contained) {
+ if (referred.isEmpty())
+ return;
+
+ referred = new TreeSet(referred);
+
+ if (bundleManifestVersion > 1) {
+ referred.addAll(contained);
+ }
+
+ StringBuffer sb = new StringBuffer();
+ String del = "";
+
+ for (Iterator i = referred.iterator(); i.hasNext();) {
+ sb.append(del);
+ sb.append(i.next());
+ del = ", ";
+ }
+ archiveConfig.addManifestEntry("Import-Package", sb.toString());
+ }
+
+ /**
+ * Calculate the bundle class path based on the list of JARs in our bundle.
+ * This list includes outselves. We also calculate the Bundle-Classpath
+ * header (a bit clumsy) This is a bit cheap, so maybe this needs to be
+ * changed TODO
+ *
+ * @param mainJar
+ * @param sb
+ * @return
+ */
+ List getBundleClassPath(Jar mainJar) {
+ List result = new ArrayList();
+ for (Iterator i = mainJar.resources.keySet().iterator(); i.hasNext();) {
+ String path = (String) i.next();
+ Object resource = mainJar.resources.get(path);
+ if (resource instanceof Jar) {
+ result.add(path);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * We traverse through al the classes that we can find and calculate the
+ * contained and referred set and uses.
+ *
+ * @param jar
+ * @param contained
+ * @param referred
+ * @param uses
+ */
+ void analyzeJar(Jar jar, Set contained, Set referred, Map uses) {
+ String prefix = "";
+ Set set = jar.getEntryPaths(prefix);
+ for (Iterator r = set.iterator(); r.hasNext();) {
+ String path = (String) r.next();
+ Object resource = jar.getEntry(path);
+ if (resource instanceof Clazz) {
+ Clazz clazz = (Clazz) resource;
+ String pathOfClass = path.substring(prefix.length());
+ String pack = Clazz.getPackage(pathOfClass);
+ contained.add(pack);
+ referred.addAll(clazz.getReferred());
+
+ // Add all the used packages
+ // to this package
+ Set t = (Set) uses.get(pack);
+ if (t == null)
+ uses.put(pack, t = new HashSet());
+ t.addAll(clazz.getReferred());
+ t.remove(pack);
+ }
+ }
+ }
+
+ /**
+ * Print a standard Map based OSGi header.
+ *
+ * @param exports map { name => Map { attribute|directive => value } }
+ * @return the clauses
+ */
+
+ String printClauses(Map exports) {
+ StringBuffer sb = new StringBuffer();
+ String del = "";
+ for (Iterator i = exports.keySet().iterator(); i.hasNext();) {
+ String name = (String) i.next();
+ Map map = (Map) exports.get(name);
+ sb.append(del);
+ sb.append(name);
+
+ for (Iterator j = map.keySet().iterator(); j.hasNext();) {
+ String key = (String) j.next();
+ String value = (String) map.get(key);
+ sb.append(";");
+ sb.append(key);
+ sb.append("=");
+ sb.append(value);
+ }
+ del = ", ";
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Check if the BundleManifest version is set correctly, base the manifest
+ * version on it.
+ *
+ * @throws MojoExecutionException
+ */
+ void verifyDeclaredBundleManifestVersion() throws MojoExecutionException {
+ String mfv = osgiManifest.getBundleManifestVersion();
+ if (mfv != null && mfv.trim().length() != 0) {
+ try {
+ bundleManifestVersion = Integer.parseInt(mfv);
+ if (bundleManifestVersion != 2)
+ throw new MojoExecutionException(
+ "Bundle-ManifestVersion must be 2, it is " + mfv);
+ }
+ catch (Exception e) {
+ throw new MojoExecutionException(
+ "Bundle-ManifestVersion must be an integer: " + mfv);
+ }
+ }
+ }
+
+ /**
+ * TODO: Decide if we accept merging of entire manifest.mf files Here's a
+ * big question to make a final decision at some point: Do accept merging of
+ * manifest entries located in some file somewhere in the project directory?
+ * If so, do we allow both file and configuration based entries to be
+ * specified simultaneously and how do we merge these?
+ */
+ private void addManifestFile() {
+ if (manifestFile != null) {
+ File file = new File(project.getBasedir().getAbsolutePath(),
+ manifestFile);
+ getLog().info(
+ "Manifest file: " + file.getAbsolutePath()
+ + " will be used");
+ archiveConfig.setManifestFile(file);
+ }
+ else {
+ getLog().info("No manifest file specified. Default will be used.");
+ }
+ }
+
+ /**
+ * Look for any OSGi specified manifest entries in the maven-osgi-plugin
+ * configuration section of the POM. If we find some, then add them to the
+ * target artifact's manifest.
+ */
+ private void addManifestEntries() {
+ if (osgiManifest != null && osgiManifest.getEntries().size() > 0) {
+ Map entries = osgiManifest.getEntries();
+
+ getLog().info(
+ "Bundle manifest will be modified with the following entries: "
+ + entries.toString());
+ archiveConfig.addManifestEntries(entries);
+ }
+ else {
+ getLog()
+ .info(
+ "No OSGi bundle manifest entries have been specified in the POM.");
+ }
+ }
+
+ /**
+ * We are going to iterate through the POM's specified JAR dependencies. If
+ * a dependency has a scope of either RUNTIME or COMPILE, then we'll JAR
+ * them up inside the OSGi bundle artifact. We will then add the
+ * Bundle-Classpath manifest entry.
+ */
+ private void addEmbeddedJars() throws MojoExecutionException {
+ Set artifacts = project.getArtifacts();
+
+ for (Iterator it = artifacts.iterator(); it.hasNext();) {
+ Artifact artifact = (Artifact) it.next();
+ if (!Artifact.SCOPE_PROVIDED.equals(artifact.getScope())
+ && !Artifact.SCOPE_TEST.equals(artifact.getScope())) {
+ String type = artifact.getType();
+
+ if ("jar".equals(type)) {
+ File depFile = artifact.getFile();
+
+ try {
+ FileUtils.copyFileToDirectory(depFile, outputDirectory);
+
+ }
+ catch (Exception e) {
+ String errmsg = "Error copying "
+ + depFile.getAbsolutePath() + " to "
+ + outputDirectory.getAbsolutePath();
+ throw new MojoExecutionException(errmsg, e);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Auto-set the bundle version.
+ */
+ private void addBundleVersion() {
+ // Maven uses a '-' to separate the version qualifier,
+ // while OSGi uses a '.', so we need to convert to a '.'
+ StringBuffer sb = new StringBuffer(project.getVersion());
+ if (sb.indexOf("-") >= 0) {
+ sb.setCharAt(sb.indexOf("-"), '.');
+ }
+ archiveConfig.addManifestEntry("Bundle-Version", sb.toString());
+ }
+
+ /**
+ * Returns a string array of the includes to be used when assembling/copying
+ * the war.
+ *
+ * @return an array of tokens to include
+ */
+ private String[] getIncludes() {
+ return new String[] {jarSourceIncludes};
+ }
+
+ /**
+ * Returns a string array of the excludes to be used when assembling/copying
+ * the jar.
+ *
+ * @return an array of tokens to exclude
+ */
+ private String[] getExcludes() {
+ List excludeList = new ArrayList(FileUtils.getDefaultExcludesAsList());
+
+ if (jarSourceExcludes != null && !"".equals(jarSourceExcludes)) {
+ excludeList.add(jarSourceExcludes);
+ }
- return (String[]) excludeList.toArray( EMPTY_STRING_ARRAY );
- }
+ return (String[]) excludeList.toArray(EMPTY_STRING_ARRAY);
+ }
}
Added: incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/QuotedTokenizer.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/QuotedTokenizer.java?rev=409086&view=auto
==============================================================================
--- incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/QuotedTokenizer.java (added)
+++ incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/QuotedTokenizer.java Wed May 24 00:42:30 2006
@@ -0,0 +1,114 @@
+package org.apache.felix.tools.maven.plugin;
+
+import java.util.*;
+
+public class QuotedTokenizer {
+ String string;
+ int index = 0;
+ String separators;
+ boolean returnTokens;
+ boolean ignoreWhiteSpace = true;
+ String peek;
+ char separator;
+
+ public QuotedTokenizer(String string, String separators, boolean returnTokens ) {
+ if ( string == null )
+ throw new IllegalArgumentException("string argument must be not null");
+ this.string = string;
+ this.separators = separators;
+ this.returnTokens = returnTokens;
+ }
+ public QuotedTokenizer(String string, String separators) {
+ this(string,separators,false);
+ }
+
+ public String nextToken(String separators) {
+ separator = 0;
+ if ( peek != null ) {
+ String tmp = peek;
+ peek = null;
+ return tmp;
+ }
+
+ if ( index == string.length())
+ return null;
+
+ StringBuffer sb = new StringBuffer();
+
+ while (index < string.length()) {
+ char c = string.charAt(index++);
+
+ if ( Character.isWhitespace(c)) {
+ if ( index == string.length())
+ break;
+ else
+ continue;
+ }
+
+ if (separators.indexOf(c) >= 0) {
+ if (returnTokens)
+ peek = Character.toString(c);
+ else
+ separator = c;
+ break;
+ }
+
+ switch (c) {
+ case '"' :
+ case '\'' :
+ quotedString(sb, c);
+ break;
+
+ default :
+ sb.append(c);
+ }
+ }
+ String result = sb.toString().trim();
+ if ( result.length()==0 && index==string.length())
+ return null;
+ return result;
+ }
+
+ public String nextToken() {
+ return nextToken(separators);
+ }
+
+ private void quotedString(StringBuffer sb, char c) {
+ char quote = c;
+ while (index < string.length()) {
+ c = string.charAt(index++);
+ if (c == quote)
+ break;
+ if (c == '\\' && index < string.length()
+ && string.charAt(index + 1) == quote)
+ c = string.charAt(index++);
+ sb.append(c);
+ }
+ }
+
+ public String[] getTokens() {
+ return getTokens(0);
+ }
+
+ private String [] getTokens(int cnt){
+ String token = nextToken();
+ if ( token == null )
+ return new String[cnt];
+
+ String result[] = getTokens(cnt+1);
+ result[cnt]=token;
+ return result;
+ }
+
+ public char getSeparator() { return separator; }
+
+ public List getTokenSet() {
+ List list = new ArrayList();
+ String token = nextToken();
+ while ( token != null ) {
+ list.add(token);
+ token = nextToken();
+ }
+ return list;
+ }
+}
Propchange: incubator/felix/trunk/tools/maven2/maven-osgi-plugin/src/main/java/org/apache/felix/tools/maven/plugin/QuotedTokenizer.java
------------------------------------------------------------------------------
svn:eol-style = native