You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 09:14:49 UTC

[sling-maven-launchpad-plugin] 06/49: Unifying bundle addition / removal code (re: SLING-1321)

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag maven-launchpad-plugin-2.0.10
in repository https://gitbox.apache.org/repos/asf/sling-maven-launchpad-plugin.git

commit 5682b2d1d34800af0835d1831dbe9ad301421fa8
Author: Justin Edelson <ju...@apache.org>
AuthorDate: Wed Feb 3 19:08:36 2010 +0000

    Unifying bundle addition / removal code (re: SLING-1321)
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/maven/maven-launchpad-plugin@906180 13f79535-47bb-0310-9956-ffa450edef68
---
 .../projectsupport/AbstractBundleListMojo.java     | 332 ++++++++-------------
 .../AbstractLaunchpadFrameworkMojo.java            |  76 +++++
 .../maven/projectsupport/ArtifactDefinition.java   |  21 +-
 .../maven/projectsupport/AttachBundleListMojo.java |  42 ++-
 .../CheckBundleListForSnapshotsMojo.java           |  60 ++--
 .../maven/projectsupport/CreateBundleJarMojo.java  |  14 +-
 .../CreateKarafFeatureDescriptorMojo.java          | 188 ++++--------
 .../maven/projectsupport/PreparePackageMojo.java   | 140 ++++-----
 .../projectsupport/bundlelist/BaseBundle.java      |  33 ++
 .../projectsupport/bundlelist/BaseBundleList.java  |  92 ++++++
 .../projectsupport/bundlelist/BaseStartLevel.java  |  60 ++++
 src/main/mdo/bundle-list.xml                       |   3 +
 .../projectsupport/PreparePackageMojoTest.java     |   4 +-
 13 files changed, 589 insertions(+), 476 deletions(-)

diff --git a/src/main/java/org/apache/sling/maven/projectsupport/AbstractBundleListMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/AbstractBundleListMojo.java
index 8aa3ae8..0c2fe4c 100644
--- a/src/main/java/org/apache/sling/maven/projectsupport/AbstractBundleListMojo.java
+++ b/src/main/java/org/apache/sling/maven/projectsupport/AbstractBundleListMojo.java
@@ -35,75 +35,72 @@ import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.MavenProjectHelper;
-import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.Bundle;
 import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.BundleList;
-import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.StartLevel;
 import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.io.xpp3.BundleListXpp3Reader;
-import org.codehaus.plexus.archiver.ArchiverException;
-import org.codehaus.plexus.archiver.UnArchiver;
-import org.codehaus.plexus.archiver.manager.ArchiverManager;
-import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
-import org.codehaus.plexus.components.io.fileselectors.IncludeExcludeFileSelector;
-import org.codehaus.plexus.util.FileUtils;
 import org.codehaus.plexus.util.StringUtils;
 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
 
 public abstract class AbstractBundleListMojo extends AbstractMojo {
 
     /**
-     * The name of the directory within the output directory into which the base
-     * JAR should be installed.
-     *
-     * @parameter default-value="resources"
+     * JAR Packaging type.
      */
-    protected String baseDestination;
+    protected static final String JAR = "jar";
 
     /**
-     * The directory which contains the start-level bundle directories.
-     *
-     * @parameter default-value="bundles"
+     * WAR Packaging type.
      */
-    protected String bundlesDirectory;
+    protected static final String WAR = "war";
+
+    protected static boolean shouldCopy(File source, File dest) {
+        if (!dest.exists()) {
+            return true;
+        } else {
+            return source.lastModified() > dest.lastModified();
+        }
+    }
 
     /**
-     * The definition of the defaultBundleList artifact.
-     *
-     * @parameter
+     * @parameter default-value="${basedir}/src/main/bundles/list.xml"
      */
-    private ArtifactDefinition defaultBundleList;
+    protected File bundleListFile;
 
     /**
-     * The definition of the defaultBundles package.
+     * The definition of the defaultBundleList artifact.
      *
      * @parameter
      */
-    private ArtifactDefinition defaultBundles;
+    protected ArtifactDefinition defaultBundleList;
 
     /**
-     * @parameter default-value="${basedir}/src/main/bundles/list.xml"
+     * The Maven project.
+     *
+     * @parameter expression="${project}"
+     * @required
+     * @readonly
      */
-    protected File bundleListFile;
+    protected MavenProject project;
 
     /**
-     * To look up Archiver/UnArchiver implementations
-     *
      * @component
      */
-    private ArchiverManager archiverManager;
+    protected MavenProjectHelper projectHelper;
 
     /**
-     * @component
+     * Any additional bundles to include in the project's bundles directory.
+     *
+     * @parameter
      */
-    protected MavenProjectHelper projectHelper;
+    private ArtifactDefinition[] additionalBundles;
+
+    private BundleList bundleList;
 
     /**
-     * The Maven project.
+     * Bundles which should be removed from the project's bundles directory.
      *
-     * @parameter expression="${project}"
-     * @required
-     * @readonly
+     * @parameter
      */
-    protected MavenProject project;
+    private ArtifactDefinition[] bundleExclusions;
 
     /**
      * Used to look up Artifacts in the remote repository.
@@ -113,6 +110,13 @@ public abstract class AbstractBundleListMojo extends AbstractMojo {
     private ArtifactFactory factory;
 
     /**
+     * If true, include the default bundles.
+     *
+     * @parameter default-value="true"
+     */
+    private boolean includeDefaultBundles;
+
+    /**
      * Location of the local repository.
      *
      * @parameter expression="${localRepository}"
@@ -122,16 +126,6 @@ public abstract class AbstractBundleListMojo extends AbstractMojo {
     private ArtifactRepository local;
 
     /**
-     * JAR Packaging type.
-     */
-    protected static final String JAR = "jar";
-
-    /**
-     * WAR Packaging type.
-     */
-    protected static final String WAR = "war";
-
-    /**
      * List of Remote Repositories used by the resolver.
      *
      * @parameter expression="${project.remoteArtifactRepositories}"
@@ -147,31 +141,41 @@ public abstract class AbstractBundleListMojo extends AbstractMojo {
      */
     private ArtifactResolver resolver;
 
-    public final void execute() throws MojoFailureException,
-            MojoExecutionException {
+    public final void execute() throws MojoFailureException, MojoExecutionException {
         try {
-            initArtifactDefinitions();
-        } catch (IOException e) {
-            throw new MojoExecutionException(
-                    "Unable to load dependency information from properties file.",
-                    e);
+            initBundleList();
+        } catch (Exception e) {
+            throw new MojoExecutionException("Unable to load dependency information from properties file.", e);
         }
         executeWithArtifacts();
 
     }
 
-    protected abstract void executeWithArtifacts()
-            throws MojoExecutionException, MojoFailureException;
+    /**
+     * Execute the logic of the plugin after the default artifacts have been
+     * initialized.
+     */
+    protected abstract void executeWithArtifacts() throws MojoExecutionException, MojoFailureException;
 
-    protected Artifact getArtifact(ArtifactDefinition bundle)
-            throws MojoExecutionException {
-        return getArtifact(bundle.getGroupId(), bundle.getArtifactId(), bundle
-                .getVersion(), bundle.getType() != null ? bundle.getType()
-                : JAR, bundle.getClassifier());
+    /**
+     * Get a resolved Artifact from the coordinates found in the artifact
+     * definition.
+     *
+     * @param def the artifact definition
+     * @return the artifact, which has been resolved
+     * @throws MojoExecutionException
+     */
+    protected Artifact getArtifact(ArtifactDefinition def) throws MojoExecutionException {
+        return getArtifact(def.getGroupId(), def.getArtifactId(), def.getVersion(), def.getType(), def.getClassifier());
     }
 
-    protected Artifact getArtifact(String groupId, String artifactId,
-            String version, String type, String classifier)
+    /**
+     * Get a resolved Artifact from the coordinates provided
+     *
+     * @return the artifact, which has been resolved.
+     * @throws MojoExecutionException
+     */
+    protected Artifact getArtifact(String groupId, String artifactId, String version, String type, String classifier)
             throws MojoExecutionException {
         Artifact artifact;
         VersionRange vr;
@@ -183,11 +187,10 @@ public abstract class AbstractBundleListMojo extends AbstractMojo {
         }
 
         if (StringUtils.isEmpty(classifier)) {
-            artifact = factory.createDependencyArtifact(groupId, artifactId,
-                    vr, type, null, Artifact.SCOPE_COMPILE);
+            artifact = factory.createDependencyArtifact(groupId, artifactId, vr, type, null, Artifact.SCOPE_COMPILE);
         } else {
-            artifact = factory.createDependencyArtifact(groupId, artifactId,
-                    vr, type, classifier, Artifact.SCOPE_COMPILE);
+            artifact = factory.createDependencyArtifact(groupId, artifactId, vr, type, classifier,
+                    Artifact.SCOPE_COMPILE);
         }
         try {
             resolver.resolve(artifact, remoteRepos, local);
@@ -199,127 +202,80 @@ public abstract class AbstractBundleListMojo extends AbstractMojo {
         return artifact;
     }
 
-    protected final void initArtifactDefinitions() throws IOException {
-        Properties dependencies = new Properties();
-        dependencies
-                .load(getClass()
-                        .getResourceAsStream(
-                                "/org/apache/sling/maven/projectsupport/dependencies.properties"));
-
-        if (defaultBundles == null) {
-            defaultBundles = new ArtifactDefinition();
-        }
-        defaultBundles.initDefaults(dependencies.getProperty("defaultBundles"));
-
-        if (defaultBundleList == null) {
-            defaultBundleList = new ArtifactDefinition();
-        }
-        defaultBundleList.initDefaults(dependencies.getProperty("defaultBundleList"));
-
-        initArtifactDefinitions(dependencies);
+    protected BundleList getBundleList() {
+        return bundleList;
     }
 
+    /**
+     * Hook methods for subclasses to initialize any additional artifact
+     * definitions.
+     *
+     * @param dependencies the dependency properties loaded from the JAR file
+     */
     protected void initArtifactDefinitions(Properties dependencies) {
     }
 
-    protected void copy(ArtifactDefinition additionalBundle,
-            File outputDirectory) throws MojoExecutionException {
-        Artifact artifact = getArtifact(additionalBundle);
-        copy(artifact.getFile(), additionalBundle.getStartLevel(),
-                outputDirectory);
+    /**
+     * Hook methods for subclasses to initialize the bundle list.
+     */
+    protected void initBundleList(BundleList bundleList) {
     }
 
-    protected void copy(File file, int startLevel, File outputDirectory)
-            throws MojoExecutionException {
-        File destination = new File(outputDirectory, String.format(
-                "%s/%s/%s/%s", baseDestination, bundlesDirectory, startLevel,
-                file.getName()));
-        if (shouldCopy(file, destination)) {
-            getLog().info(
-                    String.format("Copying bundle from %s to %s", file
-                            .getPath(), destination.getPath()));
-            try {
-                FileUtils.copyFile(file, destination);
-            } catch (IOException e) {
-                throw new MojoExecutionException("Unable to copy bundle from "
-                        + file.getPath(), e);
-            }
-        }
+    protected boolean isCurrentArtifact(ArtifactDefinition def) {
+        return (def.getGroupId().equals(project.getGroupId()) && def.getArtifactId().equals(project.getArtifactId()));
     }
 
+    /**
+     * Initialize the artifact definitions using defaults inside the plugin JAR.
+     *
+     * @throws IOException if the default properties can't be read
+     * @throws XmlPullParserException
+     * @throws MojoExecutionException
+     */
+    private final void initArtifactDefinitions() throws IOException {
+        Properties dependencies = new Properties();
+        dependencies.load(getClass().getResourceAsStream(
+                "/org/apache/sling/maven/projectsupport/dependencies.properties"));
 
-    protected BundleList readBundleList() throws IOException, XmlPullParserException {
-        return readBundleList(bundleListFile);
-    }
+        if (defaultBundleList == null) {
+            defaultBundleList = new ArtifactDefinition();
+        }
+        defaultBundleList.initDefaults(dependencies.getProperty("defaultBundleList"));
 
+        initArtifactDefinitions(dependencies);
+    }
 
-    protected void outputBundleList(File outputDirectory)
-            throws MojoExecutionException {
-        try {
-            if (bundleListFile != null && bundleListFile.exists()) {
-                getLog().info(
-                        "Using bundle list file from "
-                                + bundleListFile.getAbsolutePath());
-                BundleList bundles = readBundleList(bundleListFile);
-                copyBundles(bundles, outputDirectory);
-                return;
+    private final void initBundleList() throws IOException, XmlPullParserException, MojoExecutionException {
+        initArtifactDefinitions();
+        if (isCurrentArtifact(defaultBundleList)) {
+            bundleList = readBundleList(bundleListFile);
+        } else {
+            bundleList = new BundleList();
+            if (includeDefaultBundles) {
+                Artifact artifact = getArtifact(defaultBundleList.getGroupId(), defaultBundleList.getArtifactId(),
+                        defaultBundleList.getVersion(), defaultBundleList.getType(), defaultBundleList.getClassifier());
+                getLog().info("Using bundle list file from " + artifact.getFile().getAbsolutePath());
+                bundleList = readBundleList(artifact.getFile());
             }
-        } catch (Exception e) {
-            getLog()
-                    .warn(
-                            String
-                                    .format(
-                                            "Unable to use bundle list from %s. Falling back to bundles artifact.",
-                                            bundleListFile), e);
-        }
 
-        if (!isCurrentArtifact(defaultBundleList)) {
-            try {
-                Artifact artifact = getArtifact(defaultBundleList.getGroupId(),
-                        defaultBundleList.getArtifactId(), defaultBundleList
-                                .getVersion(), defaultBundleList.getType(),
-                        defaultBundleList.getClassifier());
-                getLog().info(
-                        "Using bundle list file from "
-                                + artifact.getFile().getAbsolutePath());
-                BundleList bundles = readBundleList(artifact.getFile());
-                copyBundles(bundles, outputDirectory);
-                return;
-            } catch (Exception e) {
-                getLog()
-                        .warn(
-                                "Unable to load bundle list from artifact. Falling back to bundle jar",
-                                e);
+            if (bundleListFile.exists()) {
+                bundleList.merge(readBundleList(bundleListFile));
             }
         }
-
-        if (!isCurrentArtifact(defaultBundleList)) {
-            Artifact defaultBundlesArtifact = getArtifact(defaultBundles
-                    .getGroupId(), defaultBundles.getArtifactId(),
-                    defaultBundles.getVersion(), defaultBundles.getType(),
-                    defaultBundles.getClassifier());
-            unpack(defaultBundlesArtifact.getFile(), outputDirectory, null,
-                    "META-INF/**");
+        if (additionalBundles != null) {
+            for (ArtifactDefinition def : additionalBundles) {
+                bundleList.add(def.toBundle());
+            }
         }
-    }
-
-    private boolean isCurrentArtifact(ArtifactDefinition def) {
-        return (def.getGroupId().equals(project.getGroupId()) && def
-                .getArtifactId().equals(project.getArtifactId()));
-    }
-
-    private void copyBundles(BundleList bundles, File outputDirectory)
-            throws MojoExecutionException {
-        for (StartLevel startLevel : bundles.getStartLevels()) {
-            for (Bundle bundle : startLevel.getBundles()) {
-                copy(new ArtifactDefinition(bundle, startLevel.getLevel()),
-                        outputDirectory);
+        if (bundleExclusions != null) {
+            for (ArtifactDefinition def : bundleExclusions) {
+                bundleList.remove(def.toBundle(), false);
             }
         }
+        initBundleList(bundleList);
     }
 
-    protected BundleList readBundleList(File file) throws IOException,
-            XmlPullParserException {
+    private BundleList readBundleList(File file) throws IOException, XmlPullParserException {
         BundleListXpp3Reader reader = new BundleListXpp3Reader();
         FileInputStream fis = new FileInputStream(file);
         try {
@@ -329,50 +285,4 @@ public abstract class AbstractBundleListMojo extends AbstractMojo {
         }
     }
 
-    protected boolean shouldCopy(File source, File dest) {
-        if (!dest.exists()) {
-            return true;
-        } else {
-            return source.lastModified() > dest.lastModified();
-        }
-    }
-
-    protected void unpack(File source, File destination, String includes,
-            String excludes) throws MojoExecutionException {
-        getLog().info(
-                "Unpacking " + source.getPath() + " to\n  "
-                        + destination.getPath());
-        try {
-            destination.mkdirs();
-
-            UnArchiver unArchiver = archiverManager.getUnArchiver(source);
-
-            unArchiver.setSourceFile(source);
-            unArchiver.setDestDirectory(destination);
-
-            if (StringUtils.isNotEmpty(excludes)
-                    || StringUtils.isNotEmpty(includes)) {
-                IncludeExcludeFileSelector[] selectors = new IncludeExcludeFileSelector[] { new IncludeExcludeFileSelector() };
-
-                if (StringUtils.isNotEmpty(excludes)) {
-                    selectors[0].setExcludes(excludes.split(","));
-                }
-
-                if (StringUtils.isNotEmpty(includes)) {
-                    selectors[0].setIncludes(includes.split(","));
-                }
-
-                unArchiver.setFileSelectors(selectors);
-            }
-
-            unArchiver.extract();
-        } catch (NoSuchArchiverException e) {
-            throw new MojoExecutionException("Unable to find archiver for "
-                    + source.getPath(), e);
-        } catch (ArchiverException e) {
-            throw new MojoExecutionException("Unable to unpack "
-                    + source.getPath(), e);
-        }
-    }
-
 }
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/AbstractLaunchpadFrameworkMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/AbstractLaunchpadFrameworkMojo.java
new file mode 100644
index 0000000..7eeede7
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/AbstractLaunchpadFrameworkMojo.java
@@ -0,0 +1,76 @@
+/*
+ * 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.sling.maven.projectsupport;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.Bundle;
+import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.BundleList;
+import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.StartLevel;
+import org.codehaus.plexus.util.FileUtils;
+
+/**
+ * This class contains the Launchpad-framework specific utility methods.
+ *
+ */
+public abstract class AbstractLaunchpadFrameworkMojo extends AbstractBundleListMojo {
+
+    /**
+     * The name of the directory within the output directory into which the base
+     * JAR should be installed.
+     *
+     * @parameter default-value="resources"
+     */
+    protected String baseDestination;
+
+    /**
+     * The directory which contains the start-level bundle directories.
+     *
+     * @parameter default-value="bundles"
+     */
+    protected String bundlesDirectory;
+
+    protected void copyBundles(BundleList bundles, File outputDirectory) throws MojoExecutionException {
+        for (StartLevel startLevel : bundles.getStartLevels()) {
+            for (Bundle bundle : startLevel.getBundles()) {
+                copy(new ArtifactDefinition(bundle, startLevel.getLevel()), outputDirectory);
+            }
+        }
+    }
+
+    protected void copy(ArtifactDefinition additionalBundle, File outputDirectory) throws MojoExecutionException {
+        Artifact artifact = getArtifact(additionalBundle);
+        copy(artifact.getFile(), additionalBundle.getStartLevel(), outputDirectory);
+    }
+
+    protected void copy(File file, int startLevel, File outputDirectory) throws MojoExecutionException {
+        File destination = new File(outputDirectory, String.format("%s/%s/%s/%s", baseDestination, bundlesDirectory,
+                startLevel, file.getName()));
+        if (shouldCopy(file, destination)) {
+            getLog().info(String.format("Copying bundle from %s to %s", file.getPath(), destination.getPath()));
+            try {
+                FileUtils.copyFile(file, destination);
+            } catch (IOException e) {
+                throw new MojoExecutionException("Unable to copy bundle from " + file.getPath(), e);
+            }
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/ArtifactDefinition.java b/src/main/java/org/apache/sling/maven/projectsupport/ArtifactDefinition.java
index 1c89034..801b879 100644
--- a/src/main/java/org/apache/sling/maven/projectsupport/ArtifactDefinition.java
+++ b/src/main/java/org/apache/sling/maven/projectsupport/ArtifactDefinition.java
@@ -41,7 +41,7 @@ public class ArtifactDefinition {
 
     /** The artifact version */
     private String version;
-    
+
     public ArtifactDefinition() {
     }
 
@@ -113,10 +113,10 @@ public class ArtifactDefinition {
      * Initialize this ArtifactDefinition with a set of default values from a
      * comma-delimited string. This string must have 6 items in it:
      * [groupId],[artifactId],[version],[type],[classifier],[startLevel]
-     * 
+     *
      * The only required parameter is the last one, which must be parseable as
      * an integer.
-     * 
+     *
      * @param commaDelimitedList
      *            the comma-delimited list
      */
@@ -137,7 +137,7 @@ public class ArtifactDefinition {
      * Initialize this ArtifactDefinition with a set of default values. If the
      * corresponding field in this object is null (or 0 in the case of start
      * level) and the parameter is non-null, the parameter value will be used.
-     * 
+     *
      * @param groupId
      *            the groupId
      * @param artifactId
@@ -173,4 +173,17 @@ public class ArtifactDefinition {
         }
     }
 
+    public Bundle toBundle() {
+        Bundle bnd = new Bundle();
+        bnd.setArtifactId(artifactId);
+        bnd.setGroupId(groupId);
+        bnd.setVersion(version);
+        if (type != null) {
+            bnd.setType(type);
+        }
+        bnd.setClassifier(classifier);
+        bnd.setStartLevel(startLevel);
+        return bnd;
+    }
+
 }
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/AttachBundleListMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/AttachBundleListMojo.java
index 13ede61..ddb0287 100644
--- a/src/main/java/org/apache/sling/maven/projectsupport/AttachBundleListMojo.java
+++ b/src/main/java/org/apache/sling/maven/projectsupport/AttachBundleListMojo.java
@@ -16,33 +16,51 @@
  */
 package org.apache.sling.maven.projectsupport;
 
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
+import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.io.xpp3.BundleListXpp3Writer;
 
 /**
  * Attaches the bundle list as a project artifact.
- * 
+ *
  * @goal attach-bundle-list
  * @phase package
  * @description attach the bundle list as a project artifact
  */
 public class AttachBundleListMojo extends AbstractBundleListMojo {
-    
+
     private static final String CLASSIFIER = "bundlelist";
-    
+
     private static final String TYPE = "xml";
 
+    /**
+     * @parameter default-value="${project.build.directory}/bundleList.xml"
+     */
+    private File outputFile;
+
+    private BundleListXpp3Writer writer = new BundleListXpp3Writer();
+
     @Override
-    protected void executeWithArtifacts() throws MojoExecutionException,
-            MojoFailureException {
-        if (bundleListFile != null && bundleListFile.exists()) {
-            projectHelper.attachArtifact(project, TYPE, CLASSIFIER,
-                    bundleListFile);
-        } else {
-            throw new MojoExecutionException(
-                    "The bundle list file does not exist.");
+    protected void executeWithArtifacts() throws MojoExecutionException, MojoFailureException {
+        FileWriter fw = null;
+        try {
+            fw = new FileWriter(outputFile);
+            writer.write(fw, getBundleList());
+            projectHelper.attachArtifact(project, TYPE, CLASSIFIER, outputFile);
+        } catch (IOException e) {
+            throw new MojoExecutionException("Unable to output effective bundle list", e);
+        } finally {
+            if (fw != null) {
+                try {
+                    fw.close();
+                } catch (IOException e) {
+                }
+            }
         }
-
     }
 
 }
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/CheckBundleListForSnapshotsMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/CheckBundleListForSnapshotsMojo.java
index 2982b93..cb0a210 100644
--- a/src/main/java/org/apache/sling/maven/projectsupport/CheckBundleListForSnapshotsMojo.java
+++ b/src/main/java/org/apache/sling/maven/projectsupport/CheckBundleListForSnapshotsMojo.java
@@ -16,7 +16,6 @@
  */
 package org.apache.sling.maven.projectsupport;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -25,60 +24,45 @@ import org.apache.maven.plugin.MojoFailureException;
 import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.Bundle;
 import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.BundleList;
 import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.StartLevel;
-import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
 
 /**
  * Validate that the bundle list file (if it exists) does not contain references
  * to SNAPSHOT versions.
- * 
+ *
  * @goal check-bundle-list-for-snapshots
- * 
+ *
  */
 public class CheckBundleListForSnapshotsMojo extends AbstractBundleListMojo {
 
     /**
      * True if the build should be failed if a snapshot is found.
-     * 
+     *
      * @parameter default-value="true"
      */
     private boolean failOnSnapshot;
 
     @Override
-    protected void executeWithArtifacts() throws MojoExecutionException,
-            MojoFailureException {
-        if (bundleListFile.exists()) {
-            try {
-                List<Bundle> snapshots = new ArrayList<Bundle>();
-                BundleList bundleList = readBundleList();
-                for (StartLevel level : bundleList.getStartLevels()) {
-                    for (Bundle bundle : level.getBundles()) {
-                        if (isSnapshot(bundle)) {
-                            snapshots.add(bundle);
-                        }
-                    }
+    protected void executeWithArtifacts() throws MojoExecutionException, MojoFailureException {
+        List<Bundle> snapshots = new ArrayList<Bundle>();
+        BundleList bundleList = getBundleList();
+        for (StartLevel level : bundleList.getStartLevels()) {
+            for (Bundle bundle : level.getBundles()) {
+                if (isSnapshot(bundle)) {
+                    snapshots.add(bundle);
                 }
-                if (!snapshots.isEmpty()) {
-                    getLog()
-                            .error(
-                                    "The following entries in the bundle list file are SNAPSHOTs:");
-                    for (Bundle bundle : snapshots) {
-                        getLog().error(String.format("     %s:%s:%s", bundle.getGroupId(),
-                                bundle.getArtifactId(), bundle.getVersion()));
-                    }
-                    if (failOnSnapshot) {
-                        throw new MojoFailureException("SNAPSHOTs were found in the bundle list. See log.");
-                    }
-                }
-            } catch (IOException e) {
-                throw new MojoExecutionException(
-                        "Unable to load bundle list file", e);
-            } catch (XmlPullParserException e) {
-                throw new MojoExecutionException(
-                        "Unable to load bundle list file", e);
             }
-
-        } else {
-            getLog().debug("Bundle list file does not exist. Skipping.");
+        }
+        if (!snapshots.isEmpty()) {
+            getLog().error("The following entries in the bundle list file are SNAPSHOTs:");
+            for (Bundle bundle : snapshots) {
+                getLog().error(
+                        String
+                                .format("     %s:%s:%s", bundle.getGroupId(), bundle.getArtifactId(), bundle
+                                        .getVersion()));
+            }
+            if (failOnSnapshot) {
+                throw new MojoFailureException("SNAPSHOTs were found in the bundle list. See log.");
+            }
         }
     }
 
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/CreateBundleJarMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/CreateBundleJarMojo.java
index 97b7747..01802db 100644
--- a/src/main/java/org/apache/sling/maven/projectsupport/CreateBundleJarMojo.java
+++ b/src/main/java/org/apache/sling/maven/projectsupport/CreateBundleJarMojo.java
@@ -29,7 +29,6 @@ import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.StartLevel;
 import org.codehaus.plexus.archiver.ArchiverException;
 import org.codehaus.plexus.archiver.jar.JarArchiver;
 import org.codehaus.plexus.util.DirectoryScanner;
-import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
 
 /**
  * Create and attach a JAR file containing the resolved artifacts from the
@@ -40,7 +39,7 @@ import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
  * @phase package
  *
  */
-public class CreateBundleJarMojo extends AbstractBundleListMojo {
+public class CreateBundleJarMojo extends AbstractLaunchpadFrameworkMojo {
 
 	/**
 	 * The list of resources we want to add to the bundle JAR file.
@@ -76,16 +75,7 @@ public class CreateBundleJarMojo extends AbstractBundleListMojo {
 	public static final String[] DEFAULT_INCLUDES = { "**/**" };
 
 	private void addBundles() throws MojoExecutionException {
-		BundleList bundles = null;
-		try {
-			bundles = readBundleList(bundleListFile);
-		} catch (IOException e) {
-			throw new MojoExecutionException("Unable to read bundle list file",
-					e);
-		} catch (XmlPullParserException e) {
-			throw new MojoExecutionException("Unable to read bundle list file",
-					e);
-		}
+		BundleList bundles = getBundleList();
 
 		for (StartLevel level : bundles.getStartLevels()) {
 			for (Bundle bundle : level.getBundles()) {
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/CreateKarafFeatureDescriptorMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/CreateKarafFeatureDescriptorMojo.java
index f34382e..9e7e9e3 100644
--- a/src/main/java/org/apache/sling/maven/projectsupport/CreateKarafFeatureDescriptorMojo.java
+++ b/src/main/java/org/apache/sling/maven/projectsupport/CreateKarafFeatureDescriptorMojo.java
@@ -19,16 +19,12 @@ package org.apache.sling.maven.projectsupport;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
 
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
 import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.Bundle;
 import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.BundleList;
 import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.StartLevel;
-import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
 import org.jdom.Document;
 import org.jdom.Element;
 import org.jdom.output.Format;
@@ -36,129 +32,77 @@ import org.jdom.output.XMLOutputter;
 
 /**
  * Create and attach a karaf feature descriptor XML file.
- * 
+ *
  * @goal create-karaf-descriptor
  * @phase package
  * @description create a karaf feature descriptor
  */
 public class CreateKarafFeatureDescriptorMojo extends AbstractBundleListMojo {
 
-	private static final String CLASSIFIER = "features";
-
-	private static final String TYPE = "xml";
-
-	/**
-	 * @parameter
-	 */
-	private String[] additionalBundles;
-
-	private Set<String> excludedArtifacts;
-	
-	/**
-	 * @parameter
-	 */
-	private String[] exclusions;
-	
-	/**
-	 * @parameter default-value="sling"
-	 */
-	private String featureName;
-	
-	/**
-	 * @parameter default-value="sling-2.0"
-	 */
-	private String featuresName;
-
-	/**
-	 * @parameter default-value="${project.version}"
-	 */
-	private String featureVersion;
-
-	/**
-	 * The output directory.
-	 * 
-	 * @parameter default-value="${project.build.directory}"
-	 */
-	private File outputDirectory;
-
-	@Override
-	protected void executeWithArtifacts() throws MojoExecutionException,
-			MojoFailureException {
-		Document doc = new Document();
-
-		Element features = new Element("features");
-		doc.setRootElement(features);
-		features.setAttribute("name", featuresName);
-
-		Element feature = new Element("feature");
-		features.addContent(feature);
-		feature.setAttribute("name", featureName);
-		feature.setAttribute("version", featureVersion);
-		
-		excludedArtifacts = new HashSet<String>();
-		if (exclusions != null) {
-			excludedArtifacts.addAll(Arrays.asList(exclusions));
-		}
-
-		try {
-			BundleList bundleList = readBundleList();
-			for (StartLevel level : bundleList.getStartLevels()) {
-				for (Bundle bundle : level.getBundles()) {
-					if (include(bundle)) {
-						String bundleRef = String.format("mvn:%s/%s/%s", bundle
-								.getGroupId(), bundle.getArtifactId(), bundle
-								.getVersion());
-						feature.addContent(new Element("bundle")
-								.setText(bundleRef));
-					}
-				}
-			}
-		} catch (IOException e) {
-			throw new MojoExecutionException("Unable to read bundle list file",
-					e);
-		} catch (XmlPullParserException e) {
-			throw new MojoExecutionException("Unable to read bundle list file",
-					e);
-		}
-
-		if (additionalBundles != null) {
-			for (String bundleRef : additionalBundles) {
-				Element bundle = new Element("bundle");
-				bundle.setText(bundleRef);
-				feature.addContent(bundle);
-			}
-		}
-
-		File outputFile = new File(outputDirectory, "features.xml");
-
-		FileOutputStream out = null;
-		try {
-			out = new FileOutputStream(outputFile);
-			new XMLOutputter(Format.getPrettyFormat().setEncoding("UTF-8"))
-					.output(doc, out);
-		} catch (IOException e) {
-			throw new MojoExecutionException("Unable to write features.xml", e);
-		} finally {
-			if (out != null) {
-				try {
-					out.close();
-				} catch (IOException e) {
-				}
-			}
-		}
-		projectHelper.attachArtifact(project, TYPE, CLASSIFIER, outputFile);
-
-	}
-
-	/**
-	 * Decide if the bundle should be included in the bundle list.
-	 * 
-	 * @param bundle the bundle
-	 * @return true if it should be included
-	 */
-	private boolean include(Bundle bundle) {
-		String ref = bundle.getGroupId() + ":" + bundle.getArtifactId();
-		return !excludedArtifacts.contains(ref);
-	}
+    private static final String CLASSIFIER = "features";
+
+    private static final String TYPE = "xml";
+
+    /**
+     * @parameter default-value="sling"
+     */
+    private String featureName;
+
+    /**
+     * @parameter default-value="sling-2.0"
+     */
+    private String featuresName;
+
+    /**
+     * @parameter default-value="${project.version}"
+     */
+    private String featureVersion;
+
+    /**
+     * The output directory.
+     *
+     * @parameter default-value="${project.build.directory}/features.xml"
+     */
+    private File outputFile;
+
+    @Override
+    protected void executeWithArtifacts() throws MojoExecutionException, MojoFailureException {
+        Document doc = new Document();
+
+        Element features = new Element("features");
+        doc.setRootElement(features);
+        features.setAttribute("name", featuresName);
+
+        Element feature = new Element("feature");
+        features.addContent(feature);
+        feature.setAttribute("name", featureName);
+        feature.setAttribute("version", featureVersion);
+
+        BundleList bundleList = getBundleList();
+        for (StartLevel level : bundleList.getStartLevels()) {
+            for (Bundle bundle : level.getBundles()) {
+                String bundleRef = String.format("mvn:%s/%s/%s", bundle.getGroupId(), bundle.getArtifactId(), bundle
+                        .getVersion());
+                feature.addContent(new Element("bundle").setText(bundleRef));
+            }
+        }
+
+        FileOutputStream out = null;
+        try {
+            out = new FileOutputStream(outputFile);
+            new XMLOutputter(Format.getPrettyFormat().setEncoding("UTF-8")).output(doc, out);
+            projectHelper.attachArtifact(project, TYPE, CLASSIFIER, outputFile);
+        } catch (IOException e) {
+            throw new MojoExecutionException("Unable to write features.xml", e);
+        } finally {
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+
+    }
 
 }
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/PreparePackageMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/PreparePackageMojo.java
index 262a6fa..5972e34 100644
--- a/src/main/java/org/apache/sling/maven/projectsupport/PreparePackageMojo.java
+++ b/src/main/java/org/apache/sling/maven/projectsupport/PreparePackageMojo.java
@@ -23,8 +23,14 @@ import java.util.Properties;
 import org.apache.maven.artifact.Artifact;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
-import org.codehaus.plexus.util.DirectoryScanner;
+import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.BundleList;
+import org.codehaus.plexus.archiver.ArchiverException;
+import org.codehaus.plexus.archiver.UnArchiver;
+import org.codehaus.plexus.archiver.manager.ArchiverManager;
+import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
+import org.codehaus.plexus.components.io.fileselectors.IncludeExcludeFileSelector;
 import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.StringUtils;
 
 /**
  * Initialize a Sling application project by extracting bundles into the correct
@@ -35,28 +41,7 @@ import org.codehaus.plexus.util.FileUtils;
  * @phase process-sources
  * @description initialize a Sling application project
  */
-public class PreparePackageMojo extends AbstractBundleListMojo {
-
-	/**
-	 * Any additional bundles to include in the project's bundles directory.
-	 *
-	 * @parameter
-	 */
-	private ArtifactDefinition[] additionalBundles;
-
-	/**
-	 * Bundles which should be removed from the project's bundles directory.
-	 *
-	 * @parameter
-	 */
-	private ArtifactDefinition[] bundlesToRemove;
-
-	/**
-	 * If true, install the default bundles.
-	 *
-	 * @parameter default-value="true"
-	 */
-	private boolean installDefaultBundles;
+public class PreparePackageMojo extends AbstractLaunchpadFrameworkMojo {
 
 	/**
 	 * The output directory for the default bundles in a WAR-packaged project,
@@ -97,45 +82,43 @@ public class PreparePackageMojo extends AbstractBundleListMojo {
 	 */
 	private File buildOutputDirectory;
 
+    /**
+     * To look up Archiver/UnArchiver implementations
+     *
+     * @component
+     */
+    private ArchiverManager archiverManager;
+
 	public void executeWithArtifacts() throws MojoExecutionException, MojoFailureException {
 		copyBaseArtifact();
-		if (installDefaultBundles) {
-			unpackDefaultBundles();
-		}
-		copyAdditionalBundles();
-		copyWebSupportBundle();
-		removeBundles();
+		copyBundles(getBundleList(), getOutputDirectory());
 		if (JAR.equals(packaging)) {
 			unpackBaseArtifact();
 		}
 	}
 
-	private void removeBundles() throws MojoExecutionException {
-	    if (bundlesToRemove != null) {
-	        File bundleBaseDir = new File(getOutputDirectory(), String.format(
-	                "%s/%s", baseDestination, bundlesDirectory));
-
-	        for (ArtifactDefinition def : bundlesToRemove) {
-	            DirectoryScanner scanner = new DirectoryScanner();
-	            scanner.setBasedir(bundleBaseDir);
-	            scanner.setIncludes(new String[] { "**/" + def.getArtifactId() + "-*.*"});
-	            scanner.scan();
-	            for (String toRemove : scanner.getIncludedFiles()) {
-	                getLog().info("Deleting " + toRemove);
-	                new File(toRemove).delete();
-	            }
-	        }
-	    }
-	}
+	protected void initArtifactDefinitions(Properties dependencies) {
+		if (base == null) {
+			base = new ArtifactDefinition();
+		}
+		base.initDefaults(dependencies.getProperty("base"));
 
-	private void copyAdditionalBundles() throws MojoExecutionException {
-		if (additionalBundles != null) {
-			for (int i = 0; i < additionalBundles.length; i++) {
-				copy(additionalBundles[i], getOutputDirectory());
-			}
+		if (jarWebSupport == null) {
+			jarWebSupport = new ArtifactDefinition();
 		}
+		jarWebSupport.initDefaults(dependencies.getProperty("jarWebSupport"));
 	}
 
+	/**
+     * Add the JAR Web Support bundle to the bundle list.
+     */
+    @Override
+    protected void initBundleList(BundleList bundleList) {
+        if (packaging.equals(JAR)) {
+            bundleList.add(jarWebSupport.toBundle());
+        }
+    }
+
 	private void copyBaseArtifact() throws MojoExecutionException {
 		Artifact artifact = getBaseArtifact();
 		if (artifact == null) {
@@ -166,13 +149,6 @@ public class PreparePackageMojo extends AbstractBundleListMojo {
 		}
 	}
 
-	private void copyWebSupportBundle() throws MojoExecutionException {
-		if (JAR.equals(packaging)) {
-			copy(jarWebSupport, getOutputDirectory());
-		}
-
-	}
-
 	private Artifact getBaseArtifact() throws MojoExecutionException {
 		Artifact baseDependency = getBaseDependency();
 		if (baseDependency == null) {
@@ -198,20 +174,6 @@ public class PreparePackageMojo extends AbstractBundleListMojo {
 		}
 	}
 
-
-
-	protected void initArtifactDefinitions(Properties dependencies) {
-		if (base == null) {
-			base = new ArtifactDefinition();
-		}
-		base.initDefaults(dependencies.getProperty("base"));
-
-		if (jarWebSupport == null) {
-			jarWebSupport = new ArtifactDefinition();
-		}
-		jarWebSupport.initDefaults(dependencies.getProperty("jarWebSupport"));
-	}
-
 	private void unpackBaseArtifact() throws MojoExecutionException {
 		Artifact artifact = getBaseDependency();
 		if (artifact == null) {
@@ -224,8 +186,36 @@ public class PreparePackageMojo extends AbstractBundleListMojo {
 		unpack(artifact.getFile(), buildOutputDirectory, null, null);
 	}
 
-	private void unpackDefaultBundles() throws MojoExecutionException {
-		outputBundleList(getOutputDirectory());
+    private void unpack(File source, File destination, String includes, String excludes)
+            throws MojoExecutionException {
+        getLog().info("Unpacking " + source.getPath() + " to\n  " + destination.getPath());
+        try {
+            destination.mkdirs();
 
-	}
+            UnArchiver unArchiver = archiverManager.getUnArchiver(source);
+
+            unArchiver.setSourceFile(source);
+            unArchiver.setDestDirectory(destination);
+
+            if (StringUtils.isNotEmpty(excludes) || StringUtils.isNotEmpty(includes)) {
+                IncludeExcludeFileSelector[] selectors = new IncludeExcludeFileSelector[] { new IncludeExcludeFileSelector() };
+
+                if (StringUtils.isNotEmpty(excludes)) {
+                    selectors[0].setExcludes(excludes.split(","));
+                }
+
+                if (StringUtils.isNotEmpty(includes)) {
+                    selectors[0].setIncludes(includes.split(","));
+                }
+
+                unArchiver.setFileSelectors(selectors);
+            }
+
+            unArchiver.extract();
+        } catch (NoSuchArchiverException e) {
+            throw new MojoExecutionException("Unable to find archiver for " + source.getPath(), e);
+        } catch (ArchiverException e) {
+            throw new MojoExecutionException("Unable to unpack " + source.getPath(), e);
+        }
+    }
 }
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/bundlelist/BaseBundle.java b/src/main/java/org/apache/sling/maven/projectsupport/bundlelist/BaseBundle.java
new file mode 100644
index 0000000..5c9b683
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/bundlelist/BaseBundle.java
@@ -0,0 +1,33 @@
+/*
+ * 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.sling.maven.projectsupport.bundlelist;
+
+public abstract class BaseBundle {
+
+    public abstract String getArtifactId();
+
+    public abstract String getClassifier();
+
+    public abstract String getGroupId();
+
+    public abstract int getStartLevel();
+
+    public abstract String getType();
+
+    public abstract String getVersion();
+
+}
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/bundlelist/BaseBundleList.java b/src/main/java/org/apache/sling/maven/projectsupport/bundlelist/BaseBundleList.java
new file mode 100644
index 0000000..cd838c8
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/bundlelist/BaseBundleList.java
@@ -0,0 +1,92 @@
+/*
+ * 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.sling.maven.projectsupport.bundlelist;
+
+import java.util.List;
+
+import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.Bundle;
+import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.BundleList;
+import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.StartLevel;
+
+public abstract class BaseBundleList {
+
+    public abstract List<StartLevel> getStartLevels();
+
+    public Bundle get(Bundle bundle, boolean compareVersions) {
+        for (StartLevel sl : getStartLevels()) {
+            Bundle foundBundle = sl.getBundle(bundle, compareVersions);
+            if (foundBundle != null) {
+                return foundBundle;
+            }
+        }
+        return null;
+    }
+
+    public boolean remove(Bundle bundle, boolean compareVersions) {
+        for (StartLevel sl : getStartLevels()) {
+            if (sl.removeBundle(bundle, compareVersions)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * Merge the current bundle list with an additional list.
+     * @see add(Bundle)
+     *
+     * @param bundleList the new bundle list
+     */
+    public void merge(BundleList bundleList) {
+        for (StartLevel sl : bundleList.getStartLevels()) {
+            for (Bundle bnd : sl.getBundles()) {
+                add(bnd);
+            }
+        }
+    }
+
+    /**
+     * Add an artifact definition. If it already exists, update the version, but
+     * do not change the start level.
+     *
+     * @param newBnd the bundle to add
+     */
+    public void add(Bundle newBnd) {
+        Bundle current = get(newBnd, false);
+        if (current != null) {
+
+        } else {
+            StartLevel startLevel = getOrCreateStartLevel(newBnd.getStartLevel());
+            startLevel.getBundles().add(newBnd);
+        }
+
+    }
+
+    private StartLevel getOrCreateStartLevel(int startLevel) {
+        for (StartLevel sl : getStartLevels()) {
+            if (sl.getLevel() == startLevel) {
+                return sl;
+            }
+        }
+
+        StartLevel sl = new StartLevel();
+        sl.setLevel(startLevel);
+        return sl;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/bundlelist/BaseStartLevel.java b/src/main/java/org/apache/sling/maven/projectsupport/bundlelist/BaseStartLevel.java
new file mode 100644
index 0000000..b7e5923
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/bundlelist/BaseStartLevel.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.sling.maven.projectsupport.bundlelist;
+
+import java.util.List;
+import java.util.ListIterator;
+
+import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.Bundle;
+
+public abstract class BaseStartLevel {
+
+    public abstract List<Bundle> getBundles();
+
+    public boolean removeBundle(Bundle bundle, boolean compareVersions) {
+        for (ListIterator<Bundle> it = getBundles().listIterator(); it.hasNext();) {
+            if (isSameArtifact(bundle, it.next(), compareVersions)) {
+                it.remove();
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean containsBundle(Bundle bundle, boolean compareVersions) {
+        for (Bundle compare : getBundles()) {
+            return isSameArtifact(bundle, compare, compareVersions);
+        }
+        return false;
+    }
+
+    public Bundle getBundle(Bundle bundle, boolean compareVersions) {
+        for (Bundle compare : getBundles()) {
+            if (isSameArtifact(bundle, compare, compareVersions)) {
+                return compare;
+            }
+        }
+        return null;
+    }
+
+    private boolean isSameArtifact(Bundle bundle1, Bundle bundle2, boolean compareVersions) {
+        boolean result = compareVersions ? bundle1.getVersion().equals(bundle2) : true;
+        return result && bundle1.getArtifactId().equals(bundle2.getArtifactId())
+                && bundle1.getGroupId().equals(bundle2.getGroupId()) && bundle1.getType().equals(bundle2.getType());
+    }
+
+}
diff --git a/src/main/mdo/bundle-list.xml b/src/main/mdo/bundle-list.xml
index db071ab..8c078b2 100644
--- a/src/main/mdo/bundle-list.xml
+++ b/src/main/mdo/bundle-list.xml
@@ -35,6 +35,7 @@
             <name>BundleList</name>
             <description>List of bundles.</description>
             <version>1.0.0</version>
+            <superClass>org.apache.sling.maven.projectsupport.bundlelist.BaseBundleList</superClass>
             <fields>
                 <field>
                     <name>startLevels</name>
@@ -49,6 +50,7 @@
         <class xml.tagName="startLevel">
         	<name>StartLevel</name>
             <version>1.0.0</version>
+            <superClass>org.apache.sling.maven.projectsupport.bundlelist.BaseStartLevel</superClass>
             <fields>
             	<field xml.attribute="true">
             		<name>level</name>
@@ -68,6 +70,7 @@
         <class xml.tagName="bundle">
             <name>Bundle</name>
             <description>A bundle.</description>
+            <superClass>org.apache.sling.maven.projectsupport.bundlelist.BaseBundle</superClass>
             <fields>
                 <field>
                     <name>groupId</name>
diff --git a/src/test/java/org/apache/sling/maven/projectsupport/PreparePackageMojoTest.java b/src/test/java/org/apache/sling/maven/projectsupport/PreparePackageMojoTest.java
index c46f338..51fe815 100644
--- a/src/test/java/org/apache/sling/maven/projectsupport/PreparePackageMojoTest.java
+++ b/src/test/java/org/apache/sling/maven/projectsupport/PreparePackageMojoTest.java
@@ -35,8 +35,8 @@ public class PreparePackageMojoTest {
 		makeArtifactAssertions(mojo, "base", "org.apache.sling",
 				"org.apache.sling.launchpad.base", null, "jar", null, 0);
 
-		makeArtifactAssertions(mojo, "defaultBundles", "org.apache.sling",
-				"org.apache.sling.launchpad", "RELEASE", "jar", "bundles", 0);
+		//makeArtifactAssertions(mojo, "defaultBundles", "org.apache.sling",
+		//		"org.apache.sling.launchpad", "RELEASE", "jar", "bundles", 0);
 
         makeArtifactAssertions(mojo, "defaultBundleList", "org.apache.sling",
                 "org.apache.sling.launchpad", "RELEASE", "xml", "bundlelist", 0);

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.