Applied patch (FELIX-199) to add a recursive goal to generate OSGi 
bundles for all dependencies.


git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@527194 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/tools/maven2/maven-bundle-plugin/pom.xml b/tools/maven2/maven-bundle-plugin/pom.xml
index 425a8f3..b4f6f7b 100644
--- a/tools/maven2/maven-bundle-plugin/pom.xml
+++ b/tools/maven2/maven-bundle-plugin/pom.xml
@@ -30,13 +30,6 @@
  <groupId>org.apache.felix</groupId>
  <artifactId>maven-bundle-plugin</artifactId>
 
- <repositories>
-  <repository>
-   <id>aQute</id>
-   <url>http://www.aQute.biz/repo</url>
-   </repository>
- </repositories>
- 
  <packaging>maven-plugin</packaging>
  <name>Maven Bundle Plugin</name>
  <description> provides a maven plugin that allows that builds the jar by
@@ -48,7 +41,7 @@
  <dependencies>
   <dependency>
    <groupId>biz.aQute</groupId>
-   <artifactId>bnd</artifactId>
+   <artifactId>bndlib</artifactId>
    <version>0.0.116</version>
   </dependency>
   <dependency>
@@ -59,17 +52,28 @@
   <dependency>
    <groupId>org.apache.maven</groupId>
    <artifactId>maven-plugin-api</artifactId>
-   <version>2.0</version>
+   <version>2.0.6</version>
   </dependency>
   <dependency>
    <groupId>org.apache.maven</groupId>
    <artifactId>maven-archiver</artifactId>
-   <version>2.0</version>
+   <version>2.2</version>
   </dependency>
   <dependency>
    <groupId>org.apache.maven</groupId>
    <artifactId>maven-artifact</artifactId>
-   <version>2.0</version>
+   <version>2.0.6</version>
   </dependency>
+    <dependency>
+      <groupId>org.apache.maven.shared</groupId>
+      <artifactId>maven-dependency-tree</artifactId>
+      <version>1.0-alpha-3-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.shared</groupId>
+      <artifactId>maven-plugin-testing-harness</artifactId>
+      <version>1.0-beta-2-SNAPSHOT</version>
+      <scope>test</scope>
+    </dependency>
  </dependencies>
 </project>
diff --git a/tools/maven2/maven-bundle-plugin/src/main/java/org/apache/felix/tools/maven2/bundleplugin/BundleAllPlugin.java b/tools/maven2/maven-bundle-plugin/src/main/java/org/apache/felix/tools/maven2/bundleplugin/BundleAllPlugin.java
new file mode 100644
index 0000000..bec3498
--- /dev/null
+++ b/tools/maven2/maven-bundle-plugin/src/main/java/org/apache/felix/tools/maven2/bundleplugin/BundleAllPlugin.java
@@ -0,0 +1,459 @@
+/*
+ * 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.felix.tools.maven2.bundleplugin;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.jar.Manifest;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.factory.ArtifactFactory;
+import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.ArtifactCollector;
+import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
+import org.apache.maven.artifact.resolver.ArtifactResolutionException;
+import org.apache.maven.artifact.resolver.ArtifactResolver;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.MavenProjectBuilder;
+import org.apache.maven.project.ProjectBuildingException;
+import org.apache.maven.shared.dependency.tree.DependencyNode;
+import org.apache.maven.shared.dependency.tree.DependencyTree;
+import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder;
+import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
+
+import aQute.lib.osgi.Analyzer;
+import aQute.lib.osgi.Jar;
+
+/**
+ * 
+ * @goal bundleall
+ * @phase package
+ * @requiresDependencyResolution runtime
+ * @description build an OSGi bundle jar for all transitive dependencies
+ */
+public class BundleAllPlugin
+    extends ManifestPlugin
+{
+
+    private static final Pattern SNAPSHOT_VERSION_PATTERN = Pattern.compile( "[0-9]{8}_[0-9]{6}_[0-9]+" );
+
+    /**
+     * Local Repository.
+     *
+     * @parameter expression="${localRepository}"
+     * @required
+     * @readonly
+     */
+    private ArtifactRepository localRepository;
+
+    /**
+     * Remote repositories
+     * 
+     * @parameter expression="${project.remoteArtifactRepositories}"
+     * @required
+     * @readonly
+     */
+    private List remoteRepositories;
+
+    /**
+     * @component
+     */
+    private ArtifactFactory factory;
+
+    /**
+     * @component
+     */
+    private ArtifactMetadataSource artifactMetadataSource;
+
+    /**
+     * @component
+     */
+    private ArtifactCollector collector;
+
+    /**
+     * Artifact resolver, needed to download jars.
+     * 
+     * @component
+     */
+    private ArtifactResolver artifactResolver;
+
+    /**
+     * @component
+     */
+    private DependencyTreeBuilder dependencyTreeBuilder;
+
+    /**
+     * @component
+     */
+    private MavenProjectBuilder mavenProjectBuilder;
+
+    public void execute()
+        throws MojoExecutionException
+    {
+        BundleInfo bundleInfo = bundleAll( getProject() );
+        logDuplicatedPackages( bundleInfo );
+    }
+
+    /**
+     * Bundle a project and all its dependencies
+     * 
+     * @param project
+     * @throws MojoExecutionException
+     */
+    private BundleInfo bundleAll( MavenProject project )
+        throws MojoExecutionException
+    {
+        return bundleAll( project, Integer.MAX_VALUE );
+    }
+
+    /**
+     * Bundle a project and its transitive dependencies up to some depth level
+     * 
+     * @param project
+     * @param depth how deep to process the dependency tree
+     * @throws MojoExecutionException
+     */
+    protected BundleInfo bundleAll( MavenProject project, int depth )
+        throws MojoExecutionException
+    {
+
+        if ( alreadyBundled( project.getArtifact() ) )
+        {
+            getLog().debug( "Ignoring project already processed " + project.getArtifact() );
+            return null;
+        }
+
+        DependencyTree dependencyTree;
+
+        try
+        {
+            dependencyTree = dependencyTreeBuilder.buildDependencyTree( project, localRepository, factory,
+                                                                        artifactMetadataSource, collector );
+        }
+        catch ( DependencyTreeBuilderException e )
+        {
+            throw new MojoExecutionException( "Unable to build dependency tree", e );
+        }
+
+        getLog().debug( "Will bundle the following dependency tree\n" + dependencyTree );
+
+        BundleInfo bundleInfo = new BundleInfo();
+
+        for ( Iterator it = dependencyTree.inverseIterator(); it.hasNext(); )
+        {
+            DependencyNode node = (DependencyNode) it.next();
+            if ( !it.hasNext() )
+            {
+                /* this is the root, current project */
+                break;
+            }
+
+            Artifact artifact = resolveArtifact( node.getArtifact() );
+            node.getArtifact().setFile( artifact.getFile() );
+
+            if ( node.getDepth() > depth )
+            {
+                /* node is deeper than we want */
+                getLog().debug( "Ignoring " + node.getArtifact() + ", depth is " + node.getDepth() + ", bigger than " + depth );
+                continue;
+            }
+
+            MavenProject childProject;
+            try
+            {
+                childProject = mavenProjectBuilder.buildFromRepository( artifact, remoteRepositories, localRepository,
+                                                                        true );
+            }
+            catch ( ProjectBuildingException e )
+            {
+                throw new MojoExecutionException( "Unable to build project object for artifact " + artifact, e );
+            }
+            childProject.setArtifact( artifact );
+            getLog().debug( "Child project artifact location: " + childProject.getArtifact().getFile() );
+
+            if ( ( artifact.getScope().equals( Artifact.SCOPE_COMPILE ) )
+                || ( artifact.getScope().equals( Artifact.SCOPE_RUNTIME ) ) )
+            {
+                BundleInfo subBundleInfo = bundleAll( childProject, depth - 1 );
+                if ( subBundleInfo != null )
+                {
+                    bundleInfo.merge( subBundleInfo );
+                }
+            }
+            else
+            {
+                getLog().debug(
+                                "Not processing due to scope (" + childProject.getArtifact().getScope() + "): "
+                                    + childProject.getArtifact() );
+            }
+        }
+
+        if ( getProject() != project )
+        {
+            getLog().debug( "Project artifact location: " + project.getArtifact().getFile() );
+
+            BundleInfo subBundleInfo = bundle( project );
+            if ( subBundleInfo != null )
+            {
+                bundleInfo.merge( subBundleInfo );
+            }
+        }
+
+        return bundleInfo;
+    }
+
+    /**
+     * Bundle one project only without building its childre
+     * 
+     * @param project
+     * @throws MojoExecutionException
+     */
+    BundleInfo bundle( MavenProject project )
+        throws MojoExecutionException
+    {
+        Artifact artifact = project.getArtifact();
+        getLog().info( "Bundling " + artifact );
+
+        try
+        {
+            Map instructions = new HashMap();
+            instructions.put( Analyzer.EXPORT_PACKAGE, "*" );
+
+            project.getArtifact().setFile( getFile( artifact ) );
+            File outputFile = getOutputFile( artifact );
+
+            if ( project.getArtifact().getFile().equals( outputFile ) )
+            {
+                /* TODO find the cause why it's getting here */
+                return null;
+                //                getLog().error(
+                //                                "Trying to read and write " + artifact + " to the same file, try cleaning: "
+                //                                    + outputFile );
+                //                throw new IllegalStateException( "Trying to read and write " + artifact
+                //                    + " to the same file, try cleaning: " + outputFile );
+            }
+
+            Analyzer analyzer = getAnalyzer( project, getClasspath( project ) );
+
+            BundleInfo bundleInfo = addExportedPackages( project, analyzer.getExports().keySet() );
+
+            Jar osgiJar = new Jar( project.getArtifactId(), project.getArtifact().getFile() );
+            Manifest manifest = analyzer.getJar().getManifest();
+            osgiJar.setManifest( manifest );
+            outputFile.getParentFile().mkdirs();
+            osgiJar.write( outputFile );
+
+            return bundleInfo;
+        }
+        /* too bad Jar.write throws Exception */
+        catch ( Exception e )
+        {
+            throw new MojoExecutionException( "Error generating OSGi bundle for project "
+                + getArtifactKey( project.getArtifact() ), e );
+        }
+    }
+
+    private BundleInfo addExportedPackages( MavenProject project, Collection packages )
+    {
+        BundleInfo bundleInfo = new BundleInfo();
+        for ( Iterator it = packages.iterator(); it.hasNext(); )
+        {
+            String packageName = (String) it.next();
+            bundleInfo.addExportedPackage( packageName, project.getArtifact() );
+        }
+        return bundleInfo;
+    }
+
+    private String getArtifactKey( Artifact artifact )
+    {
+        return artifact.getGroupId() + ":" + artifact.getArtifactId();
+    }
+
+    protected String getBundleName( MavenProject project )
+    {
+        return getBundleName( project.getArtifact() );
+    }
+
+    private String getBundleNameFirstPart( Artifact artifact )
+    {
+        return artifact.getGroupId() + "." + artifact.getArtifactId();
+    }
+
+    private String getBundleName( Artifact artifact )
+    {
+        return getBundleNameFirstPart( artifact ) + "_" + convertVersionToOsgi( artifact.getVersion() ) + ".jar";
+    }
+
+    private boolean alreadyBundled( Artifact artifact )
+    {
+        return getBuiltFile( artifact ) != null;
+    }
+
+    /**
+     * Use previously built bundles when available.
+     * 
+     * @param artifact
+     */
+    protected File getFile( final Artifact artifact )
+    {
+        File bundle = getBuiltFile( artifact );
+
+        if ( bundle != null )
+        {
+            getLog().debug( "Using previously built OSGi bundle for " + artifact + " in " + bundle );
+            return bundle;
+        }
+        return super.getFile( artifact );
+    }
+
+    private File getBuiltFile( final Artifact artifact )
+    {
+        File bundle = null;
+
+        /* if bundle was already built use it instead of jar from repo */
+        File outputFile = getOutputFile( artifact );
+        if ( outputFile.exists() )
+        {
+            bundle = outputFile;
+        }
+
+        /*
+         * Find snapshots in output folder, eg. 2.1-SNAPSHOT will match 2.1.0.20070207_193904_2
+         * TODO there has to be another way to do this using Maven libs 
+         */
+        if ( ( bundle == null ) && artifact.isSnapshot() )
+        {
+            final File buildDirectory = new File( getBuildDirectory() );
+            if ( !buildDirectory.exists() )
+            {
+                buildDirectory.mkdirs();
+            }
+            File[] files = buildDirectory.listFiles( new FilenameFilter()
+            {
+                public boolean accept( File dir, String name )
+                {
+                    if ( dir.equals( buildDirectory ) && snapshotMatch( artifact, name ) )
+                    {
+                        return true;
+                    }
+                    return false;
+                }
+            } );
+            if ( files.length > 1 )
+            {
+                throw new RuntimeException( "More than one previously built bundle matches for artifact " + artifact
+                    + " : " + Arrays.asList( files ) );
+            }
+            if ( files.length == 1 )
+            {
+                bundle = files[0];
+            }
+        }
+
+        return bundle;
+    }
+
+    /**
+     * Check that the bundleName provided correspond to the artifact provided.
+     * Used to determine when the bundle name is a timestamped snapshot and the artifact is a snapshot not timestamped.
+     * 
+     * @param artifact artifact with snapshot version
+     * @param bundleName bundle file name 
+     * @return if both represent the same artifact and version, forgetting about the snapshot timestamp
+     */
+    boolean snapshotMatch( Artifact artifact, String bundleName )
+    {
+        String artifactBundleName = getBundleName( artifact );
+        int i = artifactBundleName.indexOf( "SNAPSHOT" );
+        if ( i < 0 )
+        {
+            return false;
+        }
+        artifactBundleName = artifactBundleName.substring( 0, i );
+
+        if ( bundleName.startsWith( artifactBundleName ) )
+        {
+            /* it's the same artifact groupId and artifactId */
+            String timestamp = bundleName.substring( artifactBundleName.length(), bundleName.lastIndexOf( ".jar" ) );
+            Matcher m = SNAPSHOT_VERSION_PATTERN.matcher( timestamp );
+            return m.matches();
+        }
+        return false;
+    }
+
+    protected File getOutputFile( Artifact artifact )
+    {
+        return new File( getBuildDirectory(), getBundleName( artifact ) );
+    }
+
+    private Artifact resolveArtifact( Artifact artifact )
+        throws MojoExecutionException
+    {
+        Artifact resolvedArtifact = factory.createArtifact( artifact.getGroupId(), artifact.getArtifactId(), artifact
+            .getVersion(), artifact.getScope(), artifact.getType() );
+
+        try
+        {
+            artifactResolver.resolve( resolvedArtifact, remoteRepositories, localRepository );
+        }
+        catch ( ArtifactNotFoundException e )
+        {
+            throw new MojoExecutionException( "Artifact was not found in the repo" + resolvedArtifact, e );
+        }
+        catch ( ArtifactResolutionException e )
+        {
+            throw new MojoExecutionException( "Error resolving artifact " + resolvedArtifact, e );
+        }
+
+        return resolvedArtifact;
+    }
+
+    /**
+     * Log what packages are exported in more than one bundle
+     */
+    protected void logDuplicatedPackages( BundleInfo bundleInfo )
+    {
+        Map duplicatedExports = bundleInfo.getDuplicatedExports();
+
+        for ( Iterator it = duplicatedExports.entrySet().iterator(); it.hasNext(); )
+        {
+            Map.Entry entry = (Map.Entry) it.next();
+            String packageName = (String) entry.getKey();
+            Collection artifacts = (Collection) entry.getValue();
+
+            getLog().warn( "Package " + packageName + " is exported in more than a bundle: " );
+            for ( Iterator it2 = artifacts.iterator(); it2.hasNext(); )
+            {
+                Artifact artifact = (Artifact) it2.next();
+                getLog().warn( "  " + artifact );
+            }
+
+        }
+    }
+}
diff --git a/tools/maven2/maven-bundle-plugin/src/main/java/org/apache/felix/tools/maven2/bundleplugin/BundleInfo.java b/tools/maven2/maven-bundle-plugin/src/main/java/org/apache/felix/tools/maven2/bundleplugin/BundleInfo.java
new file mode 100644
index 0000000..351c702
--- /dev/null
+++ b/tools/maven2/maven-bundle-plugin/src/main/java/org/apache/felix/tools/maven2/bundleplugin/BundleInfo.java
@@ -0,0 +1,109 @@
+/*
+ * 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.felix.tools.maven2.bundleplugin;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.maven.artifact.Artifact;
+
+/**
+ * Information result of the bundling process 
+ * 
+ * @author <a href="mailto:carlos@apache.org">Carlos Sanchez</a>
+ * @version $Id$
+ */
+public class BundleInfo
+{
+
+    /**
+     * {@link Map} &lt; {@link String}, {@link Set} &lt; {@link Artifact} > >
+     * Used to check for duplicated exports. Key is package name and value list of artifacts where it's exported.
+     */
+    private Map exportedPackages = new HashMap();
+
+    public void addExportedPackage( String packageName, Artifact artifact )
+    {
+        Set artifacts = (Set) getExportedPackages().get( packageName );
+        if ( artifacts == null )
+        {
+            artifacts = new HashSet();
+            exportedPackages.put( packageName, artifacts );
+        }
+        artifacts.add( artifact );
+    }
+
+    private Map getExportedPackages()
+    {
+        return exportedPackages;
+    }
+
+    /**
+     * Get a list of packages that are exported in more than one bundle.
+     * Key is package name and value list of artifacts where it's exported.
+     * @return {@link Map} &lt; {@link String}, {@link Set} &lt; {@link Artifact} > >
+     */
+    public Map getDuplicatedExports()
+    {
+        Map duplicatedExports = new HashMap();
+
+        for ( Iterator it = getExportedPackages().entrySet().iterator(); it.hasNext(); )
+        {
+            Map.Entry entry = (Map.Entry) it.next();
+            Set artifacts = (Set) entry.getValue();
+            if ( artifacts.size() > 1 )
+            {
+                /* remove warnings caused by different versions of same artifact */
+                Set artifactKeys = new HashSet();
+
+                String packageName = (String) entry.getKey();
+                for ( Iterator it2 = artifacts.iterator(); it2.hasNext(); )
+                {
+                    Artifact artifact = (Artifact) it2.next();
+                    artifactKeys.add( artifact.getGroupId() + "." + artifact.getArtifactId() );
+                }
+
+                if ( artifactKeys.size() > 1 )
+                {
+                    duplicatedExports.put( packageName, artifacts );
+                }
+            }
+        }
+
+        return duplicatedExports;
+    }
+
+    public void merge( BundleInfo bundleInfo )
+    {
+        for ( Iterator it = bundleInfo.getExportedPackages().entrySet().iterator(); it.hasNext(); )
+        {
+            Map.Entry entry = (Map.Entry) it.next();
+            String packageName = (String) entry.getKey();
+            Collection artifacts = (Collection) entry.getValue();
+
+            Collection artifactsWithPackage = (Collection) getExportedPackages().get( packageName );
+            if ( artifactsWithPackage == null )
+            {
+                artifactsWithPackage = new HashSet();
+                getExportedPackages().put( packageName, artifactsWithPackage );
+            }
+            artifactsWithPackage.addAll( artifacts );
+        }
+    }
+}
diff --git a/tools/maven2/maven-bundle-plugin/src/main/java/org/apache/felix/tools/maven2/bundleplugin/BundlePlugin.java b/tools/maven2/maven-bundle-plugin/src/main/java/org/apache/felix/tools/maven2/bundleplugin/BundlePlugin.java
index b6bacfe..982fe58 100644
--- a/tools/maven2/maven-bundle-plugin/src/main/java/org/apache/felix/tools/maven2/bundleplugin/BundlePlugin.java
+++ b/tools/maven2/maven-bundle-plugin/src/main/java/org/apache/felix/tools/maven2/bundleplugin/BundlePlugin.java
@@ -40,6 +40,15 @@
  */
 public class BundlePlugin extends AbstractMojo {
  
+ /** Bundle-Version must match this pattern */
+ private static final Pattern OSGI_VERSION_PATTERN = Pattern.compile("[0-9]+(\\.[0-9]+(\\.[0-9]+(\\.[0-9A-Za-z_-]+)?)?)?");
+
+ /** pattern used to change - to . */
+ //private static final Pattern P_VERSION = Pattern.compile("([0-9]+(\\.[0-9])*)-(.*)");
+
+ /** pattern that matches strings that contain only numbers */
+ private static final Pattern ONLY_NUMBERS = Pattern.compile("[0-9]+");
+
  /**
   * @parameter expression="${project.build.outputDirectory}"
   * @required
@@ -79,74 +88,50 @@
   */
  private Map    instructions = new HashMap();
  
+ protected MavenProject getProject() {
+  return project;
+ }
+
  public void execute() throws MojoExecutionException {
+  Properties properties = new Properties();
+
+  if (new File(baseDir, "src/main/resources").exists()) {
+    header(properties, Analyzer.INCLUDE_RESOURCE, "src/main/resources/");
+  }
+  
+  execute(project, instructions, properties);
+ }
+
+ protected void execute(MavenProject project, Map instructions, Properties properties) throws MojoExecutionException {
   try {
-   File jarFile = new File(buildDirectory, project.getBuild()
-     .getFinalName()
-     + ".jar");
- 
-   // Setup defaults
+    execute(project, instructions, properties, getClasspath(project));
+  }
+  catch ( IOException e ) {
+    throw new MojoExecutionException("Error calculating classpath for project " + project, e);
+  }
+ }
+
+ protected void execute(MavenProject project, Map instructions, Properties properties, Jar[] classpath) throws MojoExecutionException {
+  try {
+   File jarFile = new File(getBuildDirectory(), getBundleName(project));
+
+   properties.putAll(getDefaultProperties(project));
+   
    String bsn = project.getGroupId() + "." + project.getArtifactId();
-   Properties properties = new Properties();
-   properties.put(Analyzer.BUNDLE_SYMBOLICNAME, bsn);
-   properties.put(Analyzer.IMPORT_PACKAGE, "*");
    if (!instructions.containsKey(Analyzer.PRIVATE_PACKAGE)) {
      properties.put(Analyzer.EXPORT_PACKAGE, bsn + ".*");
    }
-   String version = project.getVersion();
-   Pattern P_VERSION = Pattern.compile("([0-9]+(\\.[0-9]+)*)-(.*)");
-   Matcher m = P_VERSION.matcher(version);
-   if (m.matches()) {
-     version = m.group(1) + "." + m.group(3);
-   }
-   properties.put(Analyzer.BUNDLE_VERSION, version);
-   header(properties, Analyzer.BUNDLE_DESCRIPTION, project
-     .getDescription());
-   header(properties, Analyzer.BUNDLE_LICENSE, printLicenses(project
-     .getLicenses()));
-   header(properties, Analyzer.BUNDLE_NAME, project.getName());
-   
-   if (project.getOrganization() != null) {
-     header(properties, Analyzer.BUNDLE_VENDOR, project
-       .getOrganization().getName());
-     if (project.getOrganization().getUrl() != null) {
-       header(properties, Analyzer.BUNDLE_DOCURL, project
-         .getOrganization().getUrl());
-     }
-   }
 
-   if (new File(baseDir, "src/main/resources").exists()) {
-     header(properties, Analyzer.INCLUDE_RESOURCE, "src/main/resources/");
-   }
+   properties.putAll(instructions);
  
-   properties.putAll(project.getProperties());
-   properties.putAll(project.getModel().getProperties());
-   properties.putAll( getProperies("project.build.", project.getBuild()));
-   properties.putAll( getProperies("pom.", project.getModel()));
-   properties.putAll( getProperies("project.", project));
-   properties.put("project.baseDir", baseDir );
-   properties.put("project.build.directory", buildDirectory );
-   properties.put("project.build.outputdirectory", outputDirectory );
-   
-   Iterator i = instructions.entrySet().iterator();
-   while (i.hasNext()) {
-     Map.Entry e = (Map.Entry)i.next();
-     String key = (String)e.getKey();
-     if (key.startsWith("_")) {
-       key = "-"+key.substring(1);
-     }
-     properties.put(key, e.getValue());
-   }
-
    Builder builder = new Builder();
    builder.setBase(baseDir);
-   Jar[] cp = getClasspath();
    builder.setProperties(properties);
-   builder.setClasspath(cp);
+   builder.setClasspath(classpath);
  
    builder.build();
    Jar jar = builder.getJar();
-   doMavenMetadata(jar);
+   doMavenMetadata(project, jar);
    builder.setJar(jar);
  
    List errors = builder.getErrors();
@@ -156,7 +141,7 @@
     jarFile.delete();
     for (Iterator e = errors.iterator(); e.hasNext();) {
      String msg = (String) e.next();
-     getLog().error(msg);
+     getLog().error("Error building bundle " + project.getArtifact() + " : " + msg);
     }
     throw new MojoFailureException("Found errors, see log");
    }
@@ -167,7 +152,7 @@
    }
    for (Iterator w = warnings.iterator(); w.hasNext();) {
     String msg = (String) w.next();
-    getLog().warn(msg);
+    getLog().warn("Warning building bundle " + project.getArtifact() + " : " + msg);
    }
  
   }
@@ -177,14 +162,14 @@
   }
  }
  
- private Map getProperies(String prefix, Object model) {
+ private Map getProperies(Model projectModel, String prefix, Object model) {
   Map properties = new HashMap();
   Method methods[] = Model.class.getDeclaredMethods();
   for (int i = 0; i < methods.length; i++) {
    String name = methods[i].getName();
    if ( name.startsWith("get") ) {
     try {
-     Object v = methods[i].invoke(project.getModel(), null );
+     Object v = methods[i].invoke(projectModel, null );
      if ( v != null ) {
       name = prefix + Character.toLowerCase(name.charAt(3)) + name.substring(4);
       if ( v.getClass().isArray() )
@@ -221,7 +206,7 @@
   * @param jar
   * @throws IOException
   */
- private void doMavenMetadata(Jar jar) throws IOException {
+ private void doMavenMetadata(MavenProject project, Jar jar) throws IOException {
   String path = "META-INF/maven/" + project.getGroupId() + "/"
     + project.getArtifactId();
   File pomFile = new File(baseDir, "pom.xml");
@@ -242,20 +227,24 @@
   * @throws ZipException
   * @throws IOException
   */
- private Jar[] getClasspath() throws ZipException, IOException {
+ protected Jar[] getClasspath(MavenProject project) throws ZipException, IOException {
   List list = new ArrayList();
   
   if (outputDirectory != null && outputDirectory.exists()) {
     list.add(new Jar(".", outputDirectory));
   }
  
-  Set artifacts = project.getArtifacts();
+  Set artifacts = project.getDependencyArtifacts();
   for (Iterator it = artifacts.iterator(); it.hasNext();) {
    Artifact artifact = (Artifact) it.next();
    if (Artifact.SCOPE_COMPILE.equals(artifact.getScope()) 
      || Artifact.SCOPE_SYSTEM.equals(artifact.getScope()) 
      || Artifact.SCOPE_PROVIDED.equals(artifact.getScope())) {
-    Jar jar = new Jar(artifact.getArtifactId(), artifact.getFile());
+    File file = getFile(artifact);
+    if (file == null) {
+        throw new RuntimeException("File is not available for artifact " + artifact + " in project " + project.getArtifact());
+    }
+    Jar jar = new Jar(artifact.getArtifactId(), file);
     list.add(jar);
    }
   }
@@ -264,6 +253,15 @@
   return cp;
  }
  
+ /**
+  * Get the file for an Artifact
+  * 
+  * @param artifact
+  */
+ protected File getFile(Artifact artifact) {
+  return artifact.getFile();
+ }
+
  private void header(Properties properties, String key, Object value) {
   if (value == null)
    return;
@@ -273,4 +271,170 @@
  
   properties.put(key, value.toString());
  }
+
+ /**
+  * Convert a Maven version into an OSGi compliant version
+  * 
+  * @param version Maven version
+  * @return the OSGi version
+  */
+ protected String convertVersionToOsgi(String version)
+ {
+     String osgiVersion;
+
+//     Matcher m = P_VERSION.matcher(version);
+//     if (m.matches()) {
+//         osgiVersion = m.group(1) + "." + m.group(3);
+//     }
+
+     /* TODO need a regexp guru here */
+
+     Matcher m;
+     
+     /* if it's already OSGi compliant don't touch it */
+     m = OSGI_VERSION_PATTERN.matcher(version);
+     if (m.matches()) {
+         return version;
+     }
+
+     osgiVersion = version;
+
+     /* check for dated snapshot versions with only major or major and minor */
+     Pattern DATED_SNAPSHOT = Pattern.compile("([0-9])(\\.([0-9]))?(\\.([0-9]))?\\-([0-9]{8}\\.[0-9]{6}\\-[0-9]*)");
+     m = DATED_SNAPSHOT.matcher(osgiVersion);
+     if (m.matches()) {
+         String major = m.group(1);
+         String minor = (m.group(3) != null) ? m.group(3) : "0";
+         String service = (m.group(5) != null) ? m.group(5) : "0";
+         String qualifier = m.group(6).replaceAll( "-", "_" ).replaceAll( "\\.", "_" );
+         osgiVersion = major + "." + minor + "." + service + "." + qualifier;
+     }
+
+     /* else transform first - to . and others to _ */
+     osgiVersion = osgiVersion.replaceFirst( "-", "\\." );
+     osgiVersion = osgiVersion.replaceAll( "-", "_" );
+     m = OSGI_VERSION_PATTERN.matcher(osgiVersion);
+     if (m.matches()) {
+         return osgiVersion;
+     }
+
+     /* remove dots in the middle of the qualifier */
+     Pattern DOTS_IN_QUALIFIER = Pattern.compile("([0-9])(\\.[0-9])?\\.([0-9A-Za-z_-]+)\\.([0-9A-Za-z_-]+)");
+     m = DOTS_IN_QUALIFIER.matcher(osgiVersion);
+     if (m.matches()) {
+         String s1 = m.group(1);
+         String s2 = m.group(2);
+         String s3 = m.group(3);
+         String s4 = m.group(4);
+
+         Matcher qualifierMatcher = ONLY_NUMBERS.matcher( s3 );
+         /* if last portion before dot is only numbers then it's not in the middle of the qualifier */
+         if (!qualifierMatcher.matches()) {
+             osgiVersion = s1 + s2 + "." + s3 + "_" + s4;
+         }
+     }
+
+     /* convert 1.string into 1.0.0.string and 1.2.string into 1.2.0.string */
+     Pattern NEED_TO_FILL_ZEROS = Pattern.compile("([0-9])(\\.([0-9]))?\\.([0-9A-Za-z_-]+)");
+     m = NEED_TO_FILL_ZEROS.matcher(osgiVersion);
+     if (m.matches()) {
+         String major = m.group(1);
+         String minor = ( m.group( 3 ) != null ) ? m.group( 3 ) : "0";
+         String service = "0";
+         String qualifier = m.group(4);
+
+         Matcher qualifierMatcher = ONLY_NUMBERS.matcher( qualifier );
+         /* if last portion is only numbers then it's not a qualifier */
+         if (!qualifierMatcher.matches()) {
+             osgiVersion = major + "." + minor + "." + service + "." + qualifier;
+         }
+     }
+
+     m = OSGI_VERSION_PATTERN.matcher(osgiVersion);
+     /* if still its not OSGi version then add everything as qualifier */
+     if (!m.matches()) {
+         String major = "0";
+         String minor = "0";
+         String service = "0";
+         String qualifier = osgiVersion.replaceAll( "\\.", "_" );
+         osgiVersion = major + "." + minor + "." + service + "." + qualifier;
+     }
+
+     return osgiVersion;
+ }
+
+ protected String getBundleName(MavenProject project) {
+  return project.getBuild().getFinalName() + ".jar";
+ }
+
+ public String getBuildDirectory() {
+  return buildDirectory;
+ }
+
+ void setBuildDirectory(String buildirectory) {
+  this.buildDirectory = buildirectory;    
+ }
+
+ /**
+  * Get a list of packages inside a Jar
+  * 
+  * @param jar
+  * @return list of package names
+  */
+ public List getPackages(Jar jar) {
+  List packages = new ArrayList();
+  for (Iterator p = jar.getDirectories().entrySet().iterator(); p.hasNext();) {
+    Map.Entry directory = (Map.Entry) p.next();
+    String path = (String) directory.getKey();
+
+    String pack = path.replace('/', '.');
+    packages.add(pack);
+  }
+  return packages;
+ }
+
+ protected Properties getDefaultProperties(MavenProject project) {
+     Properties properties = new Properties();
+     // Setup defaults
+     String bsn = project.getGroupId() + "." + project.getArtifactId();
+     properties.put(Analyzer.BUNDLE_SYMBOLICNAME, bsn);
+     properties.put(Analyzer.IMPORT_PACKAGE, "*");
+
+     String version = convertVersionToOsgi(project.getVersion());
+     
+     properties.put(Analyzer.BUNDLE_VERSION, version);
+     header(properties, Analyzer.BUNDLE_DESCRIPTION, project
+       .getDescription());
+     header(properties, Analyzer.BUNDLE_LICENSE, printLicenses(project
+       .getLicenses()));
+     header(properties, Analyzer.BUNDLE_NAME, project.getName());
+     
+     if (project.getOrganization() != null) {
+       header(properties, Analyzer.BUNDLE_VENDOR, project
+         .getOrganization().getName());
+       if (project.getOrganization().getUrl() != null) {
+         header(properties, Analyzer.BUNDLE_DOCURL, project
+           .getOrganization().getUrl());
+       }
+     }
+
+     properties.putAll(project.getProperties());
+     properties.putAll(project.getModel().getProperties());
+     properties.putAll( getProperies(project.getModel(), "project.build.", project.getBuild()));
+     properties.putAll( getProperies(project.getModel(), "pom.", project.getModel()));
+     properties.putAll( getProperies(project.getModel(), "project.", project));
+     properties.put("project.baseDir", baseDir );
+     properties.put("project.build.directory", getBuildDirectory() );
+     properties.put("project.build.outputdirectory", outputDirectory );
+     
+     return properties;
+ }
+ 
+ void setBasedir(File basedir){
+     this.baseDir = basedir;
+ }
+
+ void setOutputDirectory(File outputDirectory){
+     this.outputDirectory = outputDirectory;
+ }
 }
diff --git a/tools/maven2/maven-bundle-plugin/src/main/java/org/apache/felix/tools/maven2/bundleplugin/ManifestPlugin.java b/tools/maven2/maven-bundle-plugin/src/main/java/org/apache/felix/tools/maven2/bundleplugin/ManifestPlugin.java
new file mode 100644
index 0000000..25f7304
--- /dev/null
+++ b/tools/maven2/maven-bundle-plugin/src/main/java/org/apache/felix/tools/maven2/bundleplugin/ManifestPlugin.java
@@ -0,0 +1,157 @@
+/*
+ * 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.felix.tools.maven2.bundleplugin;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.jar.Manifest;
+
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+
+import aQute.lib.osgi.Analyzer;
+import aQute.lib.osgi.Jar;
+
+/**
+ * Generate an OSGi manifest for this project
+ * 
+ * @goal manifest
+ * @phase process-classes
+ * @requiresDependencyResolution runtime
+ */
+public class ManifestPlugin
+    extends BundlePlugin
+{
+
+    /**
+     * Directory where the manifest will be written
+     * @parameter expression="${project.build.outputDirectory}/META-INF"
+     */
+    private String manifestLocation;
+
+    protected void execute( MavenProject project, Map instructions, Properties properties, Jar[] classpath )
+        throws MojoExecutionException
+    {
+        Manifest manifest;
+        try
+        {
+            manifest = getManifest( project, instructions, properties, classpath );
+        }
+        catch ( IOException e )
+        {
+            throw new MojoExecutionException( "Error trying to generate Manifest", e );
+        }
+
+        File outputFile = new File( manifestLocation + "/MANIFEST.MF" );
+
+        try
+        {
+            writeManifest( manifest, outputFile );
+        }
+        catch ( IOException e )
+        {
+            throw new MojoExecutionException( "Error trying to write Manifest to file " + outputFile, e );
+        }
+    }
+
+    public Manifest getManifest( MavenProject project, Jar[] classpath )
+        throws IOException
+    {
+        return getManifest( project, null, null, classpath );
+    }
+
+    public Manifest getManifest( MavenProject project, Map instructions, Properties properties, Jar[] classpath )
+        throws IOException
+    {
+        return getAnalyzer( project, instructions, properties, classpath ).getJar().getManifest();
+    }
+
+    protected Analyzer getAnalyzer( MavenProject project, Jar[] classpath )
+        throws IOException
+    {
+        return getAnalyzer( project, new HashMap(), new Properties(), classpath );
+    }
+
+    protected Analyzer getAnalyzer( MavenProject project, Map instructions, Properties properties, Jar[] classpath )
+        throws IOException
+    {
+        PackageVersionAnalyzer analyzer = new PackageVersionAnalyzer();
+
+        Properties props = getDefaultProperties( project );
+        props.putAll( properties );
+
+        if ( !instructions.containsKey( Analyzer.IMPORT_PACKAGE ) )
+        {
+            props.put( Analyzer.IMPORT_PACKAGE, "*" );
+        }
+
+        props.putAll( instructions );
+
+        analyzer.setProperties( props );
+
+        analyzer.setJar( project.getArtifact().getFile() );
+
+        if ( classpath != null )
+            analyzer.setClasspath( classpath );
+
+        if ( !instructions.containsKey( Analyzer.PRIVATE_PACKAGE )
+            && !instructions.containsKey( Analyzer.EXPORT_PACKAGE ) )
+        {
+            String export = analyzer.calculateExportsFromContents( analyzer.getJar() );
+            analyzer.setProperty( Analyzer.EXPORT_PACKAGE, export );
+        }
+
+        analyzer.mergeManifest( analyzer.getJar().getManifest() );
+
+        analyzer.calcManifest();
+
+        return analyzer;
+    }
+
+    public void writeManifest( Manifest manifest, File outputFile )
+        throws IOException
+    {
+        outputFile.getParentFile().mkdirs();
+
+        FileOutputStream os;
+        os = new FileOutputStream( outputFile );
+        try
+        {
+            manifest.write( os );
+        }
+        finally
+        {
+            if ( os != null )
+            {
+                try
+                {
+                    os.close();
+                }
+                catch ( IOException e )
+                {
+                    //nothing we can do here
+                }
+            }
+        }
+    }
+}
diff --git a/tools/maven2/maven-bundle-plugin/src/main/java/org/apache/felix/tools/maven2/bundleplugin/PackageVersionAnalyzer.java b/tools/maven2/maven-bundle-plugin/src/main/java/org/apache/felix/tools/maven2/bundleplugin/PackageVersionAnalyzer.java
new file mode 100644
index 0000000..ae91a53
--- /dev/null
+++ b/tools/maven2/maven-bundle-plugin/src/main/java/org/apache/felix/tools/maven2/bundleplugin/PackageVersionAnalyzer.java
@@ -0,0 +1,79 @@
+/*
+ * 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.felix.tools.maven2.bundleplugin;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
+
+import aQute.lib.osgi.Analyzer;
+import aQute.lib.osgi.Jar;
+
+/**
+ * Extension of {@link aQute.lib.osgi.Analyzer} to handle package versions
+ * 
+ * @author <a href="mailto:carlos@apache.org">Carlos Sanchez</a>
+ * @version $Id$
+ */
+public class PackageVersionAnalyzer
+    extends Analyzer
+{
+
+    /**
+     * Remove META-INF subfolders from exports and set package versions to bundle version.
+     * 
+     * @param dot
+     * @param bundleClasspath
+     * @param contained
+     * @param referred
+     * @param uses
+     * @return 
+     * @throws IOException
+     */
+    public Map analyzeBundleClasspath( Jar dot, Map bundleClasspath, Map contained, Map referred, Map uses )
+        throws IOException
+    {
+        Map classSpace = super.analyzeBundleClasspath( dot, bundleClasspath, contained, referred, uses );
+        String bundleVersion = getProperties().getProperty( BUNDLE_VERSION );
+        for ( Iterator it = contained.entrySet().iterator(); it.hasNext(); )
+        {
+            Map.Entry entry = (Map.Entry) it.next();
+
+            /* remove packages under META-INF */
+            String packageName = (String) entry.getKey();
+            if ( packageName.startsWith( "META-INF." ) )
+            {
+                it.remove();
+            }
+
+            /* set package versions to bundle version values */
+            if ( bundleVersion != null )
+            {
+                Map values = (Map) entry.getValue();
+                if ( values.get( "version" ) == null )
+                {
+                    values.put( "version", bundleVersion );
+                }
+            }
+
+        }
+        return classSpace;
+    }
+
+}
diff --git a/tools/maven2/maven-bundle-plugin/src/main/java/org/apache/felix/tools/maven2/bundleplugin/WrapPlugin.java b/tools/maven2/maven-bundle-plugin/src/main/java/org/apache/felix/tools/maven2/bundleplugin/WrapPlugin.java
new file mode 100644
index 0000000..d0a83be
--- /dev/null
+++ b/tools/maven2/maven-bundle-plugin/src/main/java/org/apache/felix/tools/maven2/bundleplugin/WrapPlugin.java
@@ -0,0 +1,41 @@
+/*
+ * 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.felix.tools.maven2.bundleplugin;
+
+import org.apache.maven.plugin.MojoExecutionException;
+
+/**
+ * 
+ * @goal wrap
+ * @phase package
+ * @requiresDependencyResolution runtime
+ * @description build an OSGi bundle jar for direct dependencies
+ */
+public class WrapPlugin
+    extends BundleAllPlugin
+{
+
+    public void execute()
+        throws MojoExecutionException
+    {
+        BundleInfo bundleInfo = bundleAll( getProject(), 1 );
+        logDuplicatedPackages( bundleInfo );
+    }
+
+}
diff --git a/tools/maven2/maven-bundle-plugin/src/test/java/org/apache/felix/tools/maven2/bundleplugin/AbstractBundlePluginTest.java b/tools/maven2/maven-bundle-plugin/src/test/java/org/apache/felix/tools/maven2/bundleplugin/AbstractBundlePluginTest.java
new file mode 100644
index 0000000..1641878
--- /dev/null
+++ b/tools/maven2/maven-bundle-plugin/src/test/java/org/apache/felix/tools/maven2/bundleplugin/AbstractBundlePluginTest.java
@@ -0,0 +1,52 @@
+package org.apache.felix.tools.maven2.bundleplugin;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+
+import org.apache.maven.plugin.testing.stubs.ArtifactStub;
+import org.codehaus.plexus.PlexusTestCase;
+
+/**
+ * Common methods for bundle plugin testing
+ * 
+ * @author <a href="mailto:carlos@apache.org">Carlos Sanchez</a>
+ * @version $Id$
+ */
+public abstract class AbstractBundlePluginTest
+    extends PlexusTestCase
+{
+
+    protected ArtifactStub getArtifactStub()
+    {
+        ArtifactStub artifact = new ArtifactStub();
+        artifact.setGroupId( "group" );
+        artifact.setArtifactId( "artifact" );
+        artifact.setVersion( "1.0" );
+        return artifact;
+    }
+
+    protected File getTestBundle()
+    {
+        String osgiBundleFileName = "org.apache.maven.maven-model_2.1.0.SNAPSHOT.jar";
+        return getTestFile( getBasedir(), "src/test/resources/" + osgiBundleFileName );
+    }
+
+}
\ No newline at end of file
diff --git a/tools/maven2/maven-bundle-plugin/src/test/java/org/apache/felix/tools/maven2/bundleplugin/BundleAllPluginTest.java b/tools/maven2/maven-bundle-plugin/src/test/java/org/apache/felix/tools/maven2/bundleplugin/BundleAllPluginTest.java
new file mode 100644
index 0000000..81d122b
--- /dev/null
+++ b/tools/maven2/maven-bundle-plugin/src/test/java/org/apache/felix/tools/maven2/bundleplugin/BundleAllPluginTest.java
@@ -0,0 +1,102 @@
+package org.apache.felix.tools.maven2.bundleplugin;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.util.Collections;
+
+import org.apache.maven.plugin.testing.stubs.ArtifactStub;
+import org.apache.maven.plugin.testing.stubs.MavenProjectStub;
+
+/**
+ * Test for {@link BundleAllPlugin}
+ * 
+ * @author <a href="mailto:carlos@apache.org">Carlos Sanchez</a>
+ * @version $Id$
+ */
+public class BundleAllPluginTest
+    extends AbstractBundlePluginTest
+{
+
+    private BundleAllPlugin plugin;
+
+    protected void setUp()
+        throws Exception
+    {
+        super.setUp();
+        init();
+    }
+
+    private void init()
+    {
+        plugin = new BundleAllPlugin();
+        File basedir = new File( getBasedir() );
+        plugin.setBasedir( basedir );
+        File buildDirectory = new File( basedir, "target" );
+        plugin.setBuildDirectory( buildDirectory.getPath() );
+        File outputDirectory = new File( buildDirectory, "classes" );
+        plugin.setOutputDirectory( outputDirectory );
+    }
+
+    public void testSnapshotMatch()
+    {
+        ArtifactStub artifact = getArtifactStub();
+        String bundleName;
+
+        artifact.setVersion( "2.1-SNAPSHOT" );
+        bundleName = "group.artifact_2.1.0.20070207_193904_2.jar";
+
+        assertTrue( plugin.snapshotMatch( artifact, bundleName ) );
+
+        artifact.setVersion( "2-SNAPSHOT" );
+        assertFalse( plugin.snapshotMatch( artifact, bundleName ) );
+
+        artifact.setArtifactId( "artifactx" );
+        artifact.setVersion( "2.1-SNAPSHOT" );
+        assertFalse( plugin.snapshotMatch( artifact, bundleName ) );
+    }
+
+//    public void testRewriting()
+//        throws Exception
+//    {
+//
+//        MavenProjectStub project = new MavenProjectStub();
+//        project.setArtifact( getArtifactStub() );
+//        project.getArtifact().setFile( getTestBundle() );
+//        project.setDependencyArtifacts( Collections.EMPTY_SET );
+//        project.setVersion( project.getArtifact().getVersion() );
+//
+//        File output = new File( plugin.getBuildDirectory(), plugin.getBundleName( project ) );
+//        boolean delete = output.delete();
+//
+//        plugin.bundle( project );
+//
+//        init();
+//        try
+//        {
+//            plugin.bundle( project );
+//            fail();
+//        }
+//        catch ( RuntimeException e )
+//        {
+//            // expected
+//        }
+//    }
+}
diff --git a/tools/maven2/maven-bundle-plugin/src/test/java/org/apache/felix/tools/maven2/bundleplugin/BundlePluginTest.java b/tools/maven2/maven-bundle-plugin/src/test/java/org/apache/felix/tools/maven2/bundleplugin/BundlePluginTest.java
new file mode 100644
index 0000000..e024b62
--- /dev/null
+++ b/tools/maven2/maven-bundle-plugin/src/test/java/org/apache/felix/tools/maven2/bundleplugin/BundlePluginTest.java
@@ -0,0 +1,151 @@
+package org.apache.felix.tools.maven2.bundleplugin;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.maven.plugin.testing.stubs.MavenProjectStub;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.archiver.ArchiverException;
+import org.codehaus.plexus.archiver.jar.JarArchiver;
+
+import aQute.lib.osgi.Analyzer;
+import aQute.lib.osgi.Jar;
+
+/**
+ * Test for {@link BundlePlugin}.
+ * 
+ * @author <a href="mailto:carlos@apache.org">Carlos Sanchez</a>
+ * @version $Id$
+ */
+public class BundlePluginTest
+    extends AbstractBundlePluginTest
+{
+
+    private BundlePlugin plugin;
+
+    protected void setUp()
+        throws Exception
+    {
+        super.setUp();
+        plugin = new BundlePlugin();
+    }
+
+    public void testConvertVersionToOsgi()
+    {
+        String osgiVersion;
+
+        osgiVersion = plugin.convertVersionToOsgi( "2.1.0-SNAPSHOT" );
+        assertEquals( "2.1.0.SNAPSHOT", osgiVersion );
+
+        osgiVersion = plugin.convertVersionToOsgi( "2.1-SNAPSHOT" );
+        assertEquals( "2.1.0.SNAPSHOT", osgiVersion );
+
+        osgiVersion = plugin.convertVersionToOsgi( "2-SNAPSHOT" );
+        assertEquals( "2.0.0.SNAPSHOT", osgiVersion );
+
+        osgiVersion = plugin.convertVersionToOsgi( "2" );
+        assertEquals( "2", osgiVersion );
+
+        osgiVersion = plugin.convertVersionToOsgi( "2.1" );
+        assertEquals( "2.1", osgiVersion );
+
+        osgiVersion = plugin.convertVersionToOsgi( "2.1.3" );
+        assertEquals( "2.1.3", osgiVersion );
+
+        osgiVersion = plugin.convertVersionToOsgi( "2.1.3.4" );
+        assertEquals( "2.1.3.4", osgiVersion );
+
+        osgiVersion = plugin.convertVersionToOsgi( "4aug2000r7-dev" );
+        assertEquals( "0.0.0.4aug2000r7_dev", osgiVersion );
+
+        osgiVersion = plugin.convertVersionToOsgi( "1.1-alpha-2" );
+        assertEquals( "1.1.0.alpha_2", osgiVersion );
+
+        osgiVersion = plugin.convertVersionToOsgi( "1.0-alpha-16-20070122.203121-13" );
+        assertEquals( "1.0.0.alpha_16_20070122_203121_13", osgiVersion );
+
+        osgiVersion = plugin.convertVersionToOsgi( "1.0-20070119.021432-1" );
+        assertEquals( "1.0.0.20070119_021432_1", osgiVersion );
+
+        osgiVersion = plugin.convertVersionToOsgi( "1-20070119.021432-1" );
+        assertEquals( "1.0.0.20070119_021432_1", osgiVersion );
+
+        osgiVersion = plugin.convertVersionToOsgi( "1.4.1-20070217.082013-7" );
+        assertEquals( "1.4.1.20070217_082013_7", osgiVersion );
+    }
+
+    public void testReadExportedModules()
+        throws Exception
+    {
+        File osgiBundleFile = getTestBundle();
+
+        assertTrue( osgiBundleFile.exists() );
+
+        MavenProject project = new MavenProjectStub();
+        project.setGroupId( "group" );
+        project.setArtifactId( "artifact" );
+        project.setVersion( "1.1.0.0" );
+
+        PackageVersionAnalyzer analyzer = new PackageVersionAnalyzer();
+        Jar jar = new Jar( "name", osgiBundleFile );
+        analyzer.setJar( jar );
+        analyzer.setClasspath( new Jar[] { jar } );
+
+        analyzer.setProperty( Analyzer.EXPORT_PACKAGE, "*" );
+        analyzer.calcManifest();
+
+        assertEquals( 3, analyzer.getExports().size() );
+    }
+
+    public void testGetPackages()
+        throws Exception
+    {
+        File jarFile = getTestFile( "target/test-jar.jar" );
+
+        createTestJar( jarFile );
+
+        Jar jar = new Jar( "testJar", jarFile );
+        List packages = plugin.getPackages( jar );
+
+        assertEquals( 4, packages.size() );
+        int i = 0;
+        assertEquals( "META-INF", packages.get( i++ ) );
+        assertEquals( "META-INF.maven.org.apache.maven.plugins.maven-bundle-plugin", packages.get( i++ ) );
+        assertEquals( "org.apache.maven.test", packages.get( i++ ) );
+        assertEquals( "org.apache.maven.test.resources", packages.get( i++ ) );
+    }
+
+    private void createTestJar( File jarFile )
+        throws ArchiverException, IOException
+    {
+        JarArchiver archiver = new JarArchiver();
+        archiver
+            .addFile( getTestFile( "target/classes/" + BundlePlugin.class.getName().replace( '.', '/' ) + ".class" ),
+                      "org/apache/maven/test/BundlePlugin.class" );
+        archiver.addFile( getTestFile( "pom.xml" ),
+                          "META-INF/maven/org.apache.maven.plugins/maven-bundle-plugin/pom.xml" );
+        archiver.addFile( getTestFile( "pom.xml" ), "org/apache/maven/test/resources/someresource" );
+        archiver.setDestFile( jarFile );
+        archiver.createArchive();
+    }
+}
diff --git a/tools/maven2/maven-bundle-plugin/src/test/resources/org.apache.maven.maven-model_2.1.0.SNAPSHOT.jar b/tools/maven2/maven-bundle-plugin/src/test/resources/org.apache.maven.maven-model_2.1.0.SNAPSHOT.jar
new file mode 100644
index 0000000..a619981
--- /dev/null
+++ b/tools/maven2/maven-bundle-plugin/src/test/resources/org.apache.maven.maven-model_2.1.0.SNAPSHOT.jar
Binary files differ