SLING-2978 - Create tooling top-level directory and move maven and ide
under it

* moved the maven directory under tooling maven
* adjusted module paths in pom.xml
* updated the scm information in the pom.xml files
* updated the scm information in the README.txt files


git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1506645 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..0df89ae
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,27 @@
+Apache Sling Maven Plugin
+
+Maven Plugin supporting the Sling Launchpad framework.
+
+Getting Started
+===============
+
+This component uses a Maven 2 (http://maven.apache.org/) build
+environment. It requires a Java 5 JDK (or higher) and Maven (http://maven.apache.org/)
+2.0.7 or later. We recommend to use the latest Maven version.
+
+If you have Maven 2 installed, you can compile and
+package the jar using the following command:
+
+    mvn package
+
+See the Maven 2 documentation for other build features.
+
+The latest source code for this component is available in the
+Subversion (http://subversion.tigris.org/) source repository of
+the Apache Software Foundation. If you have Subversion installed,
+you can checkout the latest source using the following command:
+
+    svn checkout http://svn.apache.org/repos/asf/sling/trunk/tooling/maven/maven-launchpad-plugin
+
+See the Subversion documentation for other source control features.
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..3c8a4b6
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,224 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+    <!--
+        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.
+    -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>17</version>
+        <relativePath>../../parent/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>maven-launchpad-plugin</artifactId>
+    <version>2.2.1-SNAPSHOT</version>
+    <packaging>maven-plugin</packaging>
+
+    <name>Apache Sling Launchpad Maven Plugin</name>
+    <description>
+        Maven Plugin supporting Sling Launchpad
+    </description>
+
+    <scm>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/tooling/maven/maven-launchpad-plugin</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/tooling/maven/maven-launchpad-plugin</developerConnection>
+        <url>http://svn.apache.org/viewvc/sling/trunk/tooling/maven/maven-launchpad-plugin</url>
+    </scm>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.modello</groupId>
+                <artifactId>modello-maven-plugin</artifactId>
+                <version>1.1</version>
+                <executions>
+                    <execution>
+                        <id>bundle-manifest.xml</id>
+                        <goals>
+                            <goal>java</goal>
+                            <goal>xpp3-reader</goal>
+                            <goal>xpp3-writer</goal>
+                        </goals>
+                        <configuration>
+                            <version>1.0.0</version>
+                            <models>
+                                <model>src/main/mdo/bundle-list.xml
+                                </model>
+                            </models>
+                        </configuration>
+                    </execution>
+                </executions>
+                <configuration>
+                    <packageWithVersion>true</packageWithVersion>
+                    <useJava5>true</useJava5>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>emma-maven-plugin</artifactId>
+                <configuration>
+                    <filters>
+                        <filter>-*</filter>
+                    </filters>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.codehaus.plexus</groupId>
+                <artifactId>plexus-component-metadata</artifactId>
+                <version>1.5.5</version>
+                <executions>
+                    <execution>
+                        <id>generate-metadata</id>
+                        <goals>
+                            <goal>generate-metadata</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <reporting>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-plugin-plugin</artifactId>
+                <version>2.5.1</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <configuration>
+                    <!-- No javadocs -->
+                    <excludePackageNames>
+                        org.apache.sling
+                    </excludePackageNames>
+                </configuration>
+            </plugin>
+        </plugins>
+    </reporting>
+
+    <dependencies>
+       <dependency>
+           <groupId>org.apache.maven</groupId>
+           <artifactId>maven-core</artifactId>
+           <version>3.0</version>
+       </dependency>
+       <dependency>
+           <groupId>org.apache.maven</groupId>
+           <artifactId>maven-compat</artifactId>
+           <version>3.0</version>
+       </dependency>
+        <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-plugin-api</artifactId>
+            <version>3.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.plexus</groupId>
+            <artifactId>plexus-archiver</artifactId>
+            <version>1.0-alpha-9</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.codehaus.plexus</groupId>
+                    <artifactId>plexus-container-default</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.codehaus.plexus</groupId>
+                    <artifactId>plexus-component-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.plexus</groupId>
+            <artifactId>plexus-utils</artifactId>
+            <version>1.5.15</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.4</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jdom</groupId>
+            <artifactId>jdom</artifactId>
+            <version>1.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.launchpad.api</artifactId>
+            <version>1.0.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.launchpad.base</artifactId>
+            <version>2.4.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.launchpad.base</artifactId>
+            <version>2.4.0</version>
+            <classifier>app</classifier>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <version>4.2.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven.shared</groupId>
+            <artifactId>maven-filtering</artifactId>
+            <version>1.0</version>
+            <exclusions>
+                <exclusion>
+                    <artifactId>maven-project</artifactId>
+                    <groupId>org.apache.maven</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven.shared</groupId>
+            <artifactId>maven-osgi</artifactId>
+            <version>0.2.0</version>
+            <exclusions>
+                <exclusion>
+                    <artifactId>maven-project</artifactId>
+                    <groupId>org.apache.maven</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.drools</groupId>
+            <artifactId>drools-compiler</artifactId>
+            <version>5.3.1.Final</version>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.mojo</groupId>
+            <artifactId>versions-maven-plugin</artifactId>
+            <version>1.2</version>
+            <exclusions>
+                <exclusion>
+                    <artifactId>maven-project</artifactId>
+                    <groupId>org.apache.maven</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>maven-artifact-manager</artifactId>
+                    <groupId>org.apache.maven</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/AbstractBundleListMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/AbstractBundleListMojo.java
new file mode 100644
index 0000000..a2f5566
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/AbstractBundleListMojo.java
@@ -0,0 +1,259 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.maven.projectsupport;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.factory.ArtifactFactory;
+import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
+import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+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.artifact.versioning.ArtifactVersion;
+import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
+import org.apache.maven.artifact.versioning.VersionRange;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.MavenProjectHelper;
+import org.codehaus.plexus.util.SelectorUtils;
+import org.codehaus.plexus.util.StringUtils;
+
+public abstract class AbstractBundleListMojo extends AbstractMojo {
+
+    /**
+     * Partial Bundle List type
+     */
+    protected static final String PARTIAL = "partialbundlelist";
+
+    /**
+     * @parameter default-value="${basedir}/src/main/bundles/list.xml"
+     */
+    protected File bundleListFile;
+
+    /**
+     * The Maven project.
+     *
+     * @parameter expression="${project}"
+     * @required
+     * @readonly
+     */
+    protected MavenProject project;
+
+    /**
+     * @component
+     */
+    protected MavenProjectHelper projectHelper;
+
+    /**
+     * @parameter expression="${configDirectory}"
+     *            default-value="src/main/config"
+     */
+    private File configDirectory;
+
+    /**
+     * @parameter expression="${commonSlingProps}"
+     *            default-value="src/main/sling/common.properties"
+     */
+    protected File commonSlingProps;
+
+    /**
+     * @parameter expression="${commonSlingBootstrap}"
+     *            default-value="src/main/sling/common.bootstrap.txt"
+     */
+    protected File commonSlingBootstrap;
+
+    /**
+     * @parameter expression="${webappSlingProps}"
+     *            default-value="src/main/sling/webapp.properties"
+     */
+    protected File webappSlingProps;
+
+    /**
+     * @parameter expression="${webappSlingBootstrap}"
+     *            default-value="src/main/sling/webapp.bootstrap.txt"
+     */
+    protected File webappSlingBootstrap;
+
+    /**
+     * @parameter expression="${standaloneSlingProps}"
+     *            default-value="src/main/sling/standalone.properties"
+     */
+    protected File standaloneSlingProps;
+
+    /**
+     * @parameter expression="${standaloneSlingBootstrap}"
+     *            default-value="src/main/sling/standalone.bootstrap.txt"
+     */
+    protected File standaloneSlingBootstrap;
+
+    /**
+     * @parameter expression="${ignoreBundleListConfig}"
+     *            default-value="false"
+     */
+    protected boolean ignoreBundleListConfig;
+
+    /**
+     * @parameter expression="${session}
+     * @required
+     * @readonly
+     */
+    protected MavenSession mavenSession;
+
+    /**
+     * Used to look up Artifacts in the remote repository.
+     *
+     * @component
+     */
+    private ArtifactFactory factory;
+
+    /**
+     * Used to look up Artifacts in the remote repository.
+     *
+     * @component hint="maven"
+     */
+    private ArtifactMetadataSource metadataSource;
+
+    /**
+     * Location of the local repository.
+     *
+     * @parameter expression="${localRepository}"
+     * @readonly
+     * @required
+     */
+    private ArtifactRepository local;
+
+    /**
+     * List of Remote Repositories used by the resolver.
+     *
+     * @parameter expression="${project.remoteArtifactRepositories}"
+     * @readonly
+     * @required
+     */
+    private List<ArtifactRepository> remoteRepos;
+
+    /**
+     * Used to look up Artifacts in the remote repository.
+     *
+     * @component
+     */
+    private ArtifactResolver resolver;
+
+    protected File getConfigDirectory() {
+        return this.configDirectory;
+    }
+
+    /**
+     * 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());
+    }
+
+    /**
+     * 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;
+
+                try {
+                    vr = VersionRange.createFromVersionSpec(version);
+                } catch (InvalidVersionSpecificationException e) {
+                    vr = VersionRange.createFromVersion(version);
+                }
+
+                if (StringUtils.isEmpty(classifier)) {
+                    artifact = factory.createDependencyArtifact(groupId, artifactId, vr, type, null, Artifact.SCOPE_COMPILE);
+                } else {
+                    artifact = factory.createDependencyArtifact(groupId, artifactId, vr, type, classifier,
+                            Artifact.SCOPE_COMPILE);
+                }
+
+                // This code kicks in when the version specifier is a range.
+                if (vr.getRecommendedVersion() == null) {
+                    try {
+                        List<ArtifactVersion> availVersions = metadataSource.retrieveAvailableVersions(artifact, local, remoteRepos);
+                        ArtifactVersion resolvedVersion = vr.matchVersion(availVersions);
+                        artifact.setVersion(resolvedVersion.toString());
+                    } catch (ArtifactMetadataRetrievalException e) {
+                        throw new MojoExecutionException("Unable to find version for artifact", e);
+                    }
+
+                }
+
+                try {
+                    resolver.resolve(artifact, remoteRepos, local);
+                } catch (ArtifactResolutionException e) {
+                    throw new MojoExecutionException("Unable to resolve artifact.", e);
+                } catch (ArtifactNotFoundException e) {
+                    throw new MojoExecutionException("Unable to find artifact.", e);
+                }
+                return artifact;
+            }
+
+    /**
+     * Helper method to copy a whole directory
+     */
+    protected void copyDirectory(final File source, final File target, final String[] includes, final String[] excludes)
+    throws IOException {
+        final String prefix = source.getAbsolutePath() + File.separatorChar;
+        final int prefixLength = prefix.length();
+        org.apache.commons.io.FileUtils.copyDirectory(source, target, new FileFilter() {
+
+            public boolean accept(final File file) {
+                final String path = file.getAbsolutePath().substring(prefixLength).replace(File.separatorChar, '/');
+                if ( includes != null ) {
+                    boolean matched = false;
+                    for(int i = 0; i<includes.length && !matched; i++) {
+                        if ( SelectorUtils.matchPath(includes[i], path)) {
+                            matched = true;
+                        }
+                    }
+                    if ( !matched ) {
+                        return false;
+                    }
+                }
+                if ( excludes != null ) {
+                    for(final String pattern:excludes) {
+                        if ( SelectorUtils.matchPath(pattern, path)) {
+                            return false;
+                        }
+                    }
+                }
+                return true;
+            }
+        });
+    }
+
+}
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..23d16d0
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/AbstractLaunchpadFrameworkMojo.java
@@ -0,0 +1,115 @@
+/*
+ * 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 java.util.Set;
+import java.util.TreeSet;
+
+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 AbstractUsingBundleListMojo {
+
+    /**
+     * 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"
+     */
+    private String bundlesDirectory;
+
+    /**
+     * The directory which contains the bootstrap bundle directories.
+     *
+     * @parameter
+     */
+    private String bootDirectory;
+
+    protected String getPathForArtifact(final int startLevel, final String artifactName, final String runModes) {
+        final Set<String> runModesList = new TreeSet<String>();
+        if (runModes != null ) {
+            for(final String mode : runModes.split(",")) {
+                if ( mode.trim().length() > 0 ) {
+                    runModesList.add(mode);
+                }
+            }
+        }
+        final String runModeExt;
+        if ( runModesList.size() == 0 ) {
+            runModeExt = "";
+        } else {
+            final StringBuilder sb = new StringBuilder();
+            for(final String n : runModesList ) {
+                sb.append('.');
+                sb.append(n);
+            }
+            runModeExt = sb.toString();
+        }
+        if ( startLevel == -1 && bootDirectory != null ) {
+            return String.format("%s/%s%s/1/%s", baseDestination, bootDirectory,
+                    runModeExt,
+                    artifactName);
+        }
+        return String.format("%s/%s%s/%s/%s", baseDestination, bundlesDirectory,
+                runModeExt,
+                (startLevel == -1 ? 1 : startLevel),
+                artifactName, runModeExt);
+    }
+
+    protected void copyBundles(BundleList bundles, File outputDirectory) throws MojoExecutionException {
+        for (StartLevel startLevel : bundles.getStartLevels()) {
+            for (Bundle bundle : startLevel.getBundles()) {
+                copy(new ArtifactDefinition(bundle, startLevel.getStartLevel()), outputDirectory);
+            }
+        }
+    }
+
+    protected void copy(ArtifactDefinition additionalBundle, File outputDirectory) throws MojoExecutionException {
+        Artifact artifact = getArtifact(additionalBundle);
+        copy(artifact.getFile(), additionalBundle.getStartLevel(), additionalBundle.getRunModes(), outputDirectory);
+    }
+
+    protected void copy(File file, int startLevel, String runModes, File outputDirectory) throws MojoExecutionException {
+        File destination = new File(outputDirectory, getPathForArtifact(startLevel, file.getName().replace('/', File.separatorChar), runModes));
+        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/AbstractLaunchpadStartingMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/AbstractLaunchpadStartingMojo.java
new file mode 100644
index 0000000..bb27287
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/AbstractLaunchpadStartingMojo.java
@@ -0,0 +1,352 @@
+/*
+ * 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 static org.apache.felix.framework.util.FelixConstants.LOG_LEVEL_PROP;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.felix.framework.Logger;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.shared.filtering.MavenFilteringException;
+import org.apache.maven.shared.filtering.PropertyUtils;
+import org.apache.sling.launchpad.api.LaunchpadContentProvider;
+import org.apache.sling.launchpad.base.impl.Sling;
+import org.apache.sling.launchpad.base.shared.Notifiable;
+import org.apache.sling.launchpad.base.shared.SharedConstants;
+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.osgi.framework.BundleException;
+
+public abstract class AbstractLaunchpadStartingMojo extends AbstractUsingBundleListMojo implements Notifiable {
+
+    /** Default log level setting if no set on command line (value is "INFO"). */
+    private static final int DEFAULT_LOG_LEVEL = Logger.LOG_INFO;
+
+    /** Mapping between log level numbers and names */
+    private static final String[] logLevels = { "FATAL", "ERROR", "WARN", "INFO", "DEBUG" };
+
+    /**
+     * The configuration property setting the port on which the HTTP service
+     * listens
+     */
+    private static final String PROP_PORT = "org.osgi.service.http.port";
+
+    /** Return the log level code for the string */
+    private static int toLogLevelInt(String level, int defaultLevel) {
+        for (int i = 0; i < logLevels.length; i++) {
+            if (logLevels[i].equalsIgnoreCase(level)) {
+                return i;
+            }
+        }
+
+        return defaultLevel;
+    }
+
+    /**
+     * @parameter expression="${http.port}" default-value="8080"
+     */
+    private int httpPort;
+
+    /**
+     * The definition of the package to be included to provide web support for
+     * JAR-packaged projects (i.e. pax-web).
+     *
+     * @parameter
+     */
+    private ArtifactDefinition jarWebSupport;
+
+    /**
+     * @parameter expression="${felix.log.level}"
+     */
+    private String logLevel;
+
+    /**
+     * @parameter expression="${propertiesFile}"
+     *            default-value="src/test/config/sling.properties"
+     */
+    private File propertiesFile;
+
+    /**
+     * @parameter expression="${resourceProviderRoot}"
+     *            default-value="src/test/resources"
+     */
+    private File resourceProviderRoot;
+
+    private LaunchpadContentProvider resourceProvider = new LaunchpadContentProvider() {
+
+        public Iterator<String> getChildren(String path) {
+            if (path.equals(BUNDLE_PATH_PREFIX)) {
+                final Set<String> levels = new HashSet<String>();
+                for (final StartLevel level : getInitializedBundleList().getStartLevels()) {
+                    // we treat the boot level as level 1
+                    if ( level.getStartLevel() == -1 ) {
+                        levels.add(BUNDLE_PATH_PREFIX + "/1/");
+                    } else {
+                        levels.add(BUNDLE_PATH_PREFIX + "/" + level.getLevel() + "/");
+                    }
+                }
+                return levels.iterator();
+            } else if (path.equals("resources/corebundles")) {
+                List<String> empty = Collections.emptyList();
+                return empty.iterator();
+            } else if (path.equals(CONFIG_PATH_PREFIX)) {
+                if (getConfigDirectory().exists() && getConfigDirectory().isDirectory()) {
+                    File[] configFiles = getConfigDirectory().listFiles(new FileFilter() {
+
+                        public boolean accept(File file) {
+                            return file.isFile();
+                        }
+                    });
+
+                    List<String> fileNames = new ArrayList<String>();
+                    for (File cfgFile : configFiles) {
+                        if (cfgFile.isFile()) {
+                            fileNames.add(CONFIG_PATH_PREFIX + "/" + cfgFile.getName());
+                        }
+                    }
+
+                    return fileNames.iterator();
+
+                } else {
+                    List<String> empty = Collections.emptyList();
+                    return empty.iterator();
+                }
+            } else if (path.startsWith(BUNDLE_PATH_PREFIX)) {
+                final String startLevelInfo = path.substring(BUNDLE_PATH_PREFIX.length() + 1);
+                try {
+                    final int startLevel = Integer.parseInt(startLevelInfo);
+
+                    final List<String> bundles = new ArrayList<String>();
+                    for (final StartLevel level : getInitializedBundleList().getStartLevels()) {
+                        if (level.getStartLevel() == startLevel || (startLevel == 1 && level.getStartLevel() == -1)) {
+                            for (final Bundle bundle : level.getBundles()) {
+                                final ArtifactDefinition d = new ArtifactDefinition(bundle, startLevel);
+                                try {
+                                    final Artifact artifact = getArtifact(d);
+                                    bundles.add(artifact.getFile().toURI().toURL().toExternalForm());
+                                } catch (Exception e) {
+                                    getLog().error("Unable to resolve artifact ", e);
+                                }
+                            }
+                        }
+                    }
+                    return bundles.iterator();
+
+                } catch (NumberFormatException e) {
+                    // we ignore this
+                }
+            } else if (path.equals("resources") ) {
+                final Set<String> subDirs = new HashSet<String>();
+                subDirs.add(BUNDLE_PATH_PREFIX);
+                subDirs.add(CONFIG_PATH_PREFIX);
+                subDirs.add("resources/corebundles");
+                return subDirs.iterator();
+            }
+
+            getLog().warn("un-handlable path " + path);
+            return null;
+        }
+
+        public URL getResource(String path) {
+            if (path.startsWith(CONFIG_PATH_PREFIX)) {
+                File configFile = new File(getConfigDirectory(), path.substring(CONFIG_PATH_PREFIX.length() + 1));
+                if (configFile.exists()) {
+                    try {
+                        return configFile.toURI().toURL();
+                    } catch (MalformedURLException e) {
+                        // ignore this one
+                    }
+                }
+            }
+
+            File resourceFile = new File(resourceProviderRoot, path);
+            if (resourceFile.exists()) {
+                try {
+                    return resourceFile.toURI().toURL();
+                } catch (MalformedURLException e) {
+                    getLog().error("Unable to create URL for file", e);
+                    return null;
+                }
+            } else {
+                URL fromClasspath = getClass().getResource("/" + path);
+                if (fromClasspath != null) {
+                    return fromClasspath;
+                }
+
+                try {
+                    return new URL(path);
+                } catch (MalformedURLException e) {
+                    return null;
+                }
+            }
+
+        }
+
+        public InputStream getResourceAsStream(String path) {
+            URL res = this.getResource(path);
+            if (res != null) {
+                try {
+                    return res.openStream();
+                } catch (IOException ioe) {
+                    // ignore this one
+                }
+            }
+
+            // no resource
+            return null;
+
+        }
+    };
+
+    private Sling sling;
+
+    /**
+     * @parameter expression="${sling.home}" default-value="${basedir}/sling"
+     */
+    private String slingHome;
+
+    /**
+     * @parameter default-value="true"
+     */
+    private boolean forceBundleLoad;
+
+    public void stopped() {
+        sling = null;
+    }
+
+    public void updated(File updateFile) {
+        // clear the reference to the framework
+        sling = null;
+
+        if (updateFile != null) {
+            getLog().warn("Maven Launchpad Plugin doesn't support updating the framework bundle.");
+        }
+
+        getLog().info("Restarting Framework and Sling");
+
+        try {
+            executeWithArtifacts();
+        } catch (MojoExecutionException e) {
+            getLog().error("Unable to restart Framework and Sling", e);
+            System.exit(1);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void executeWithArtifacts() throws MojoExecutionException {
+        try {
+            final Map<String, String> props = new HashMap<String, String>();
+
+            props.put(SharedConstants.SLING_HOME, slingHome);
+
+            // ensure launchpad is set
+            props.put(SharedConstants.SLING_LAUNCHPAD, slingHome);
+
+            if (forceBundleLoad) {
+                props.put(SharedConstants.FORCE_PACKAGE_BUNDLE_LOADING, "true");
+            }
+
+            // set up and configure Felix Logger
+            int logLevelNum;
+            if (logLevel == null) {
+                logLevelNum = DEFAULT_LOG_LEVEL;
+            } else {
+                logLevelNum = toLogLevelInt(logLevel, DEFAULT_LOG_LEVEL);
+            }
+            props.put(LOG_LEVEL_PROP, String.valueOf(logLevelNum));
+            // Display port number on console, in case HttpService doesn't
+            getLog().info("HTTP server port: " + httpPort);
+            props.put(PROP_PORT, String.valueOf(httpPort));
+
+            // prevent tons of needless WARN from the framework
+            Logger logger = new Logger();
+            logger.setLogLevel(Logger.LOG_ERROR);
+
+            if (propertiesFile.exists()) {
+                File tmp = null;
+                try {
+                    tmp = File.createTempFile("sling", "props");
+                    mavenFileFilter.copyFile(propertiesFile, tmp, true, project, null, true,
+                            System.getProperty("file.encoding"), mavenSession);
+                    Properties loadedProps = PropertyUtils.loadPropertyFile(tmp, null);
+                    for (Object key : loadedProps.keySet()) {
+                        props.put((String) key, (String) loadedProps.get(key));
+                    }
+                } catch (IOException e) {
+                    throw new MojoExecutionException("Unable to create filtered properties file", e);
+                } catch (MavenFilteringException e) {
+                    throw new MojoExecutionException("Unable to create filtered properties file", e);
+                } finally {
+                    if (tmp != null) {
+                        tmp.delete();
+                    }
+                }
+            }
+
+            sling = startSling(resourceProvider, props, logger);
+
+        } catch (BundleException be) {
+            getLog().error("Failed to Start OSGi framework", be);
+        }
+
+    }
+
+    protected abstract Sling startSling(LaunchpadContentProvider resourceProvider, Map<String, String> props,
+            Logger logger) throws BundleException;
+
+    protected void stopSling() {
+        if (sling != null) {
+            sling.destroy();
+        }
+    }
+
+    @Override
+    protected void initArtifactDefinitions(Properties dependencies) {
+        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) {
+        bundleList.add(jarWebSupport.toBundle());
+    }
+}
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/AbstractUsingBundleListMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/AbstractUsingBundleListMojo.java
new file mode 100644
index 0000000..41e45a4
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/AbstractUsingBundleListMojo.java
@@ -0,0 +1,552 @@
+/*
+ * 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 static org.apache.sling.maven.projectsupport.BundleListUtils.interpolateProperties;
+import static org.apache.sling.maven.projectsupport.BundleListUtils.readBundleList;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.shared.filtering.MavenFileFilter;
+import org.apache.maven.shared.filtering.MavenFilteringException;
+import org.apache.maven.shared.filtering.PropertyUtils;
+import org.apache.sling.maven.projectsupport.BundleListUtils.ArtifactDefinitionsCallback;
+import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.BundleList;
+import org.codehaus.plexus.archiver.ArchiverException;
+import org.codehaus.plexus.archiver.zip.ZipUnArchiver;
+import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+import org.drools.KnowledgeBase;
+import org.drools.KnowledgeBaseFactory;
+import org.drools.builder.KnowledgeBuilder;
+import org.drools.builder.KnowledgeBuilderError;
+import org.drools.builder.KnowledgeBuilderFactory;
+import org.drools.builder.ResourceType;
+import org.drools.io.ResourceFactory;
+import org.drools.runtime.StatefulKnowledgeSession;
+
+public abstract class AbstractUsingBundleListMojo extends AbstractBundleListMojo {
+
+    /**
+     * JAR Packaging type.
+     */
+    protected static final String JAR = "jar";
+
+    /**
+     * WAR Packaging type.
+     */
+    protected static final String WAR = "war";
+
+    protected static final String CONFIG_PATH_PREFIX = "resources/config";
+
+    protected static final String BUNDLE_PATH_PREFIX = "resources/bundles";
+
+    protected static boolean shouldCopy(File source, File dest) {
+        if (!dest.exists()) {
+            return true;
+        }
+        return source.lastModified() > dest.lastModified();
+    }
+
+    /**
+     * The definition of the defaultBundleList artifact.
+     *
+     * @parameter
+     */
+    protected ArtifactDefinition defaultBundleList;
+
+    /**
+     * Any additional bundles to include in the project's bundles directory.
+     *
+     * @parameter
+     */
+    private ArtifactDefinition[] additionalBundles;
+
+    private BundleList initializedBundleList;
+
+    /**
+     * Bundles which should be removed from the project's bundles directory.
+     *
+     * @parameter
+     */
+    private ArtifactDefinition[] bundleExclusions;
+
+    /**
+     * If true, include the default bundles.
+     *
+     * @parameter expression="${includeDefaultBundles}" default-value="true"
+     */
+    private boolean includeDefaultBundles;
+
+    /**
+     * @parameter
+     */
+    private File[] rewriteRuleFiles;
+
+    /**
+     * The list of tokens to include when copying configs
+     * from partial bundle lists.
+     *
+     * @parameter default-value="**"
+     */
+    private String[] configIncludes;
+
+    /**
+     * The list of tokens to exclude when copying the configs
+     * from partial bundle lists.
+     *
+     * @parameter
+     */
+    private String[] configExcludes;
+
+    /**
+     * The list of names to exclude when copying properties
+     * from partial bundle lists.
+     *
+     * @parameter
+     */
+    private String[] propertiesExcludes;
+
+    /**
+     * @component
+     */
+    protected MavenFileFilter mavenFileFilter;
+
+    /**
+     * The zip unarchiver.
+     *
+     * @component role="org.codehaus.plexus.archiver.UnArchiver" roleHint="zip"
+     */
+    private ZipUnArchiver zipUnarchiver;
+
+    private Properties slingProperties;
+
+    private Properties slingWebappProperties;
+
+    private Properties slingStandaloneProperties;
+
+    private String slingBootstrapCommand;
+
+    private String slingWebappBootstrapCommand;
+
+    private String slingStandaloneBootstrapCommand;
+
+    /**
+     * @parameter default-value="${project.build.directory}/tmpBundleListconfig"
+     */
+    private File tmpOutputDir;
+
+    /**
+     * @parameter default-value="${project.build.directory}/tmpConfigDir"
+     */
+    private File tempConfigDir;
+
+    private File overlayConfigDir;
+
+    public final void execute() throws MojoFailureException, MojoExecutionException {
+        try {
+            initBundleList();
+            extractConfigurations();
+        } catch (MojoExecutionException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new MojoExecutionException("Unable to load dependency information from properties file.", e);
+        }
+        executeWithArtifacts();
+
+    }
+
+    @Override
+    protected File getConfigDirectory() {
+        if ( this.overlayConfigDir != null ) {
+            return this.overlayConfigDir;
+        }
+        return super.getConfigDirectory();
+    }
+
+    /**
+     * Execute the logic of the plugin after the default artifacts have been
+     * initialized.
+     */
+    protected abstract void executeWithArtifacts() throws MojoExecutionException, MojoFailureException;
+
+    protected BundleList getInitializedBundleList() {
+        return initializedBundleList;
+    }
+
+    /**
+     * 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) {
+    }
+
+    /**
+     * Hook methods for subclasses to initialize the bundle list.
+     */
+    protected void initBundleList(BundleList bundleList) {
+    }
+
+    /**
+     * 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 {
+        BundleListUtils.initArtifactDefinitions(getClass().getClassLoader(), new ArtifactDefinitionsCallback() {
+
+            public void initArtifactDefinitions(Properties dependencies) {
+                if (defaultBundleList == null) {
+                    defaultBundleList = new ArtifactDefinition();
+                }
+                defaultBundleList.initDefaults(dependencies.getProperty("defaultBundleList"));
+
+                AbstractUsingBundleListMojo.this.initArtifactDefinitions(dependencies);
+            }
+        });
+    }
+
+    private final void initBundleList() throws IOException, XmlPullParserException, MojoExecutionException {
+        initArtifactDefinitions();
+        if (BundleListUtils.isCurrentArtifact(project, defaultBundleList)) {
+            initializedBundleList = readBundleList(bundleListFile);
+        } else {
+            initializedBundleList = new BundleList();
+            if (includeDefaultBundles) {
+                Artifact defBndListArtifact = getArtifact(defaultBundleList.getGroupId(),
+                        defaultBundleList.getArtifactId(), defaultBundleList.getVersion(), defaultBundleList.getType(),
+                        defaultBundleList.getClassifier());
+                getLog().info("Using bundle list file from " + defBndListArtifact.getFile().getAbsolutePath());
+                initializedBundleList = readBundleList(defBndListArtifact.getFile());
+            }
+
+            if (bundleListFile.exists()) {
+                initializedBundleList.merge(readBundleList(bundleListFile));
+            }
+        }
+        // add additional bundles
+        if (additionalBundles != null) {
+            for (ArtifactDefinition def : additionalBundles) {
+                initializedBundleList.add(def.toBundle());
+            }
+        }
+
+        // check for partial bundle lists
+        final Set<Artifact> dependencies = project.getDependencyArtifacts();
+        for (Artifact artifact : dependencies) {
+            if (PARTIAL.equals(artifact.getType())) {
+                getLog().info(
+                        String.format("Merging partial bundle list %s:%s:%s", artifact.getGroupId(),
+                                artifact.getArtifactId(), artifact.getVersion()));
+                initializedBundleList.merge(readBundleList(artifact.getFile()));
+            }
+        }
+
+        // handle exclusions
+        if (bundleExclusions != null) {
+            for (ArtifactDefinition def : bundleExclusions) {
+                initializedBundleList.remove(def.toBundle(), false);
+            }
+        }
+
+        initBundleList(initializedBundleList);
+
+        interpolateProperties(initializedBundleList, project, mavenSession);
+
+        rewriteBundleList(initializedBundleList);
+    }
+
+    private final void extractConfigurations() throws MojoExecutionException, IOException {
+        final Set<Artifact> dependencies = project.getDependencyArtifacts();
+        for (Artifact artifact : dependencies) {
+            if (PARTIAL.equals(artifact.getType())) {
+                extractConfiguration(artifact);
+            }
+        }
+        // copy own config files
+        if ( this.overlayConfigDir != null && super.getConfigDirectory().exists() ) {
+            copyDirectory(super.getConfigDirectory(), this.overlayConfigDir, null, FileUtils.getDefaultExcludes());
+        }
+    }
+
+    private void extractConfiguration(final Artifact artifact) throws MojoExecutionException, IOException {
+        // check for configuration artifact
+        Artifact cfgArtifact = null;
+        try {
+            cfgArtifact = getArtifact(artifact.getGroupId(),
+                    artifact.getArtifactId(),
+                    artifact.getVersion(),
+                    AttachPartialBundleListMojo.CONFIG_TYPE,
+                    AttachPartialBundleListMojo.CONFIG_CLASSIFIER);
+        } catch (final MojoExecutionException ignore) {
+            // we just ignore this
+        }
+        if ( cfgArtifact != null ) {
+            getLog().info(
+                    String.format("Merging settings from partial bundle list %s:%s:%s", cfgArtifact.getGroupId(),
+                            cfgArtifact.getArtifactId(), cfgArtifact.getVersion()));
+
+            // extract
+            zipUnarchiver.setSourceFile(cfgArtifact.getFile());
+            try {
+                this.tmpOutputDir.mkdirs();
+                zipUnarchiver.setDestDirectory(this.tmpOutputDir);
+                zipUnarchiver.extract();
+
+                final File slingDir = new File(this.tmpOutputDir, "sling");
+                this.readSlingProperties(new File(slingDir, AttachPartialBundleListMojo.SLING_COMMON_PROPS), 0);
+                this.readSlingProperties(new File(slingDir, AttachPartialBundleListMojo.SLING_WEBAPP_PROPS), 1);
+                this.readSlingProperties(new File(slingDir, AttachPartialBundleListMojo.SLING_STANDALONE_PROPS), 2);
+                this.readSlingBootstrap(new File(slingDir, AttachPartialBundleListMojo.SLING_COMMON_BOOTSTRAP), 0);
+                this.readSlingBootstrap(new File(slingDir, AttachPartialBundleListMojo.SLING_WEBAPP_BOOTSTRAP), 1);
+                this.readSlingBootstrap(new File(slingDir, AttachPartialBundleListMojo.SLING_STANDALONE_BOOTSTRAP), 2);
+
+                // and now configurations
+                final File configDir = new File(this.tmpOutputDir, "config");
+                if ( configDir.exists() ) {
+                    if ( this.overlayConfigDir == null ) {
+                        this.tempConfigDir.mkdirs();
+                        this.overlayConfigDir = this.tempConfigDir;
+                    }
+                    final String[] defaultExcludes = FileUtils.getDefaultExcludes();
+                    String[] excludes;
+                    if ( this.configExcludes != null ) {
+                        excludes = new String[defaultExcludes.length + this.configExcludes.length];
+                        System.arraycopy(defaultExcludes, 0, excludes, 0, defaultExcludes.length);
+                        System.arraycopy(this.configExcludes, 0, excludes, defaultExcludes.length, this.configExcludes.length);
+                    } else {
+                        excludes = defaultExcludes;
+                    }
+                    String[] includes = null;
+                    if ( this.configIncludes != null ) {
+                        includes = this.configIncludes;
+                    }
+                    copyDirectory(configDir, this.overlayConfigDir,
+                                    includes, excludes);
+                }
+            } catch (final ArchiverException ae) {
+                throw new MojoExecutionException("Unable to extract configuration archive.",ae);
+            } finally {
+                // and delete at the end
+                FileUtils.deleteDirectory(this.tmpOutputDir);
+            }
+        }
+    }
+
+    private void rewriteBundleList(BundleList bundleList) throws MojoExecutionException {
+        if (rewriteRuleFiles != null) {
+            KnowledgeBase knowledgeBase = createKnowledgeBase(rewriteRuleFiles);
+            StatefulKnowledgeSession session = knowledgeBase.newStatefulKnowledgeSession();
+            try {
+                session.setGlobal("mavenSession", mavenSession);
+                session.setGlobal("mavenProject", project);
+                session.insert(bundleList);
+                session.fireAllRules();
+            } finally {
+                session.dispose();
+            }
+        }
+    }
+
+    private KnowledgeBase createKnowledgeBase(File[] files) throws MojoExecutionException {
+        KnowledgeBuilder builder = KnowledgeBuilderFactory.newKnowledgeBuilder();
+        builder.add(ResourceFactory.newClassPathResource("drools-globals.drl", getClass()), ResourceType.DRL);
+        for (File file : files) {
+            getLog().info("Parsing rule file " + file.getAbsolutePath());
+            builder.add(ResourceFactory.newFileResource(file), ResourceType.DRL);
+        }
+        if (builder.hasErrors()) {
+            getLog().error("Rule errors:");
+            for (KnowledgeBuilderError error : builder.getErrors()) {
+                getLog().error(error.toString());
+            }
+            throw new MojoExecutionException("Unable to create rules. See log for details.");
+        }
+
+        KnowledgeBase base = KnowledgeBaseFactory.newKnowledgeBase();
+        base.addKnowledgePackages(builder.getKnowledgePackages());
+        return base;
+    }
+
+    private void copyProperties(final Properties source, final Properties dest) {
+        final Enumeration<Object> keys = source.keys();
+        while ( keys.hasMoreElements() ) {
+            final Object key = keys.nextElement();
+            dest.put(key, source.get(key));
+        }
+    }
+
+    private void readSlingProperties(final File propsFile, final int mode) throws MojoExecutionException {
+        if (propsFile.exists()) {
+            File tmp = null;
+            try {
+                tmp = File.createTempFile("sling", "props");
+                mavenFileFilter.copyFile(propsFile, tmp, true, project, null, true,
+                        System.getProperty("file.encoding"), mavenSession);
+                final Properties loadedProps = PropertyUtils.loadPropertyFile(tmp, null);
+                if ( mode == 0 ) {
+                    if ( this.slingProperties == null ) {
+                        this.slingProperties = loadedProps;
+                    } else {
+                        this.copyProperties(loadedProps, this.slingProperties);
+                    }
+                    filterProperties(this.slingProperties);
+                } else if ( mode == 1 ) {
+                    if ( this.slingWebappProperties == null ) {
+                        this.slingWebappProperties = loadedProps;
+                    } else {
+                        this.copyProperties(loadedProps, this.slingWebappProperties);
+                    }
+                    filterProperties(this.slingWebappProperties);
+                } else {
+                    if ( this.slingStandaloneProperties == null ) {
+                        this.slingStandaloneProperties = loadedProps;
+                    } else {
+                        this.copyProperties(loadedProps, this.slingStandaloneProperties);
+                    }
+                    filterProperties(this.slingStandaloneProperties);
+                }
+            } catch (IOException e) {
+                throw new MojoExecutionException("Unable to create filtered properties file", e);
+            } catch (MavenFilteringException e) {
+                throw new MojoExecutionException("Unable to create filtered properties file", e);
+            } finally {
+                if (tmp != null) {
+                    tmp.delete();
+                }
+            }
+        }
+    }
+
+    /**
+     * Filter properties by removing excluded properties
+     */
+    private void filterProperties(final Properties props) {
+        if ( this.propertiesExcludes != null ) {
+            for(final String name : this.propertiesExcludes) {
+                props.remove(name.trim());
+            }
+        }
+    }
+
+    protected Properties getSlingProperties(final boolean standalone) throws MojoExecutionException {
+        readSlingProperties(this.commonSlingProps, 0);
+        final Properties additionalProps = (standalone ? this.slingStandaloneProperties : this.slingWebappProperties);
+        if ( this.slingProperties == null) {
+            return additionalProps;
+        }
+        if ( additionalProps != null ) {
+            final Properties combinedProps = new Properties();
+            this.copyProperties(this.slingProperties, combinedProps);
+            this.copyProperties(additionalProps, combinedProps);
+            return combinedProps;
+        }
+        return this.slingProperties;
+    }
+
+    /**
+     * Try to read the bootstrap command file
+     * The filter is copied to a tmp location to apply filtering.
+     * @throws MojoExecutionException
+     */
+    private void readSlingBootstrap(final File bootstrapFile, final int mode) throws MojoExecutionException {
+        if (bootstrapFile.exists()) {
+            File tmp = null;
+            Reader reader = null;
+            try {
+                tmp = File.createTempFile("sling", "bootstrap");
+                mavenFileFilter.copyFile(bootstrapFile, tmp, true, project, null, true,
+                        System.getProperty("file.encoding"), mavenSession);
+                reader = new FileReader(tmp);
+                final StringBuilder sb = new StringBuilder();
+                if ( mode == 0 ) {
+                    if ( this.slingBootstrapCommand != null ) {
+                        sb.append(this.slingBootstrapCommand);
+                    }
+                } else if ( mode == 1 ) {
+                    if ( this.slingWebappBootstrapCommand != null ) {
+                        sb.append(this.slingWebappBootstrapCommand);
+                    }
+                } else {
+                    if ( this.slingStandaloneBootstrapCommand != null ) {
+                        sb.append(this.slingStandaloneBootstrapCommand);
+                    }
+                }
+                final char[] buffer = new char[2048];
+                int l;
+                while ( (l = reader.read(buffer, 0, buffer.length) ) != -1 ) {
+                    sb.append(buffer, 0, l);
+                }
+                sb.append('\n');
+                if ( mode == 0 ) {
+                    this.slingBootstrapCommand = sb.toString();
+                } else if ( mode == 1 ) {
+                    this.slingWebappBootstrapCommand = sb.toString();
+                } else {
+                    this.slingStandaloneBootstrapCommand = sb.toString();
+                }
+            } catch (final IOException e) {
+                throw new MojoExecutionException("Unable to create filtered bootstrap file", e);
+            } catch (final MavenFilteringException e) {
+                throw new MojoExecutionException("Unable to create filtered bootstrap file", e);
+            } finally {
+                if (tmp != null) {
+                    tmp.delete();
+                }
+                if ( reader != null ) {
+                    try {
+                        reader.close();
+                    } catch (final IOException ignore) {}
+                }
+            }
+        }
+    }
+
+    /**
+     * Try to read the bootstrap command file and return its content
+     * The filter is copied to a tmp location to apply filtering.
+     * @return The contents are <code>null</code>
+     * @throws MojoExecutionException
+     */
+    protected String getSlingBootstrap(final boolean standalone) throws MojoExecutionException {
+        this.readSlingBootstrap(this.commonSlingBootstrap, 0);
+        final String addCmds = (standalone ? this.slingStandaloneBootstrapCommand : this.slingWebappBootstrapCommand);
+        if ( this.slingBootstrapCommand == null ) {
+            return addCmds;
+        }
+        if ( addCmds != null ) {
+            final StringBuilder builder = new StringBuilder(this.slingBootstrapCommand);
+            builder.append('\n');
+            builder.append(addCmds);
+            return builder.toString();
+        }
+        return this.slingBootstrapCommand;
+    }
+}
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/ArtifactDefinition.java b/src/main/java/org/apache/sling/maven/projectsupport/ArtifactDefinition.java
new file mode 100644
index 0000000..2183c2e
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/ArtifactDefinition.java
@@ -0,0 +1,229 @@
+/*
+ * 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 static org.apache.sling.maven.projectsupport.BundleListUtils.nodeValue;
+
+import org.apache.maven.model.Dependency;
+import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.Bundle;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+
+/**
+ * The definition of an artifact.
+ */
+public class ArtifactDefinition {
+
+    /** The artifactId */
+    private String artifactId;
+
+    /** The classifier */
+    private String classifier;
+
+    /** The groupId */
+    private String groupId;
+
+    /** The start level at which this artifact should be started */
+    private int startLevel;
+
+    /** The artifact type */
+    private String type;
+
+    /** The artifact version */
+    private String version;
+
+    /** The artifact run modes */
+    private String runModes;
+
+    public ArtifactDefinition() {
+    }
+
+    public ArtifactDefinition(Bundle bundle, int startLevel) {
+        this.groupId = bundle.getGroupId();
+        this.artifactId = bundle.getArtifactId();
+        this.type = bundle.getType();
+        this.version = bundle.getVersion();
+        this.classifier = bundle.getClassifier();
+        this.startLevel = startLevel;
+        this.runModes = bundle.getRunModes();
+    }
+
+    public ArtifactDefinition(Xpp3Dom config) {
+        this.groupId = nodeValue(config, "groupId", null);
+        this.artifactId = nodeValue(config, "artifactId", null);
+        this.type = nodeValue(config, "type", null);
+        this.version = nodeValue(config, "version", null);
+        this.classifier = nodeValue(config, "classifier", null);
+        this.startLevel = nodeValue(config, "startLevel", 0);
+        this.runModes = nodeValue(config, "runModes", null);
+    }
+
+    public String getArtifactId() {
+        return artifactId;
+    }
+
+    public String getClassifier() {
+        return classifier;
+    }
+
+    public String getGroupId() {
+        return groupId;
+    }
+
+    public int getStartLevel() {
+        return startLevel;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public String getRunModes() {
+        return runModes;
+    }
+
+    public void setArtifactId(String artifactId) {
+        this.artifactId = artifactId;
+    }
+
+    public void setClassifier(String classifier) {
+        this.classifier = classifier;
+    }
+
+    public void setGroupId(String groupId) {
+        this.groupId = groupId;
+    }
+
+    public void setStartLevel(int startLevel) {
+        this.startLevel = startLevel;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    @Override
+    public String toString() {
+        return "ArtifactDefinition [artifactId=" + artifactId + ", classifier="
+                + classifier + ", groupId=" + groupId + ", startLevel="
+                + startLevel + ", type=" + type + ", version=" + version
+                + ", runModes=" + runModes + "]";
+    }
+
+    /**
+     * 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
+     */
+    public void initDefaults(String commaDelimitedList) {
+        String[] values = commaDelimitedList.split(",");
+        if (values.length != 6) {
+            throw new IllegalArgumentException(
+                    String
+                            .format(
+                                    "The string %s does not have the correct number of items (6).",
+                                    commaDelimitedList));
+        }
+        initDefaults(values[0], values[1], values[2], values[3], values[4],
+                Integer.valueOf(values[5]));
+    }
+
+    /**
+     * 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
+     *            the artifactId
+     * @param version
+     *            the version
+     * @param type
+     *            the artifact type
+     * @param classifier
+     *            the artifact classified
+     * @param startLevel
+     *            the start level
+     */
+    public void initDefaults(String groupId, String artifactId, String version,
+            String type, String classifier, int startLevel) {
+        if (this.groupId == null && StringUtils.isNotEmpty(groupId)) {
+            this.groupId = groupId;
+        }
+        if (this.artifactId == null && StringUtils.isNotEmpty(artifactId)) {
+            this.artifactId = artifactId;
+        }
+        if (this.version == null && StringUtils.isNotEmpty(version)) {
+            this.version = version;
+        }
+        if (this.type == null && StringUtils.isNotEmpty(groupId)) {
+            this.type = type;
+        }
+        if (this.classifier == null && StringUtils.isNotEmpty(classifier)) {
+            this.classifier = classifier;
+        }
+        if (this.startLevel == 0) {
+            this.startLevel = startLevel;
+        }
+    }
+
+    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;
+    }
+
+    public Dependency toDependency(String scope) {
+        Dependency dep = new Dependency();
+        dep.setArtifactId(artifactId);
+        dep.setGroupId(groupId);
+        dep.setVersion(version);
+        if (type != null) {
+            dep.setType(type);
+        }
+        dep.setClassifier(classifier);
+        dep.setScope(scope);
+        return dep;
+    }
+
+    public static Dependency toDependency(Bundle bundle, String scope) {
+        return new ArtifactDefinition(bundle, 0).toDependency(scope);
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/AttachBundleListMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/AttachBundleListMojo.java
new file mode 100644
index 0000000..fe9d538
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/AttachBundleListMojo.java
@@ -0,0 +1,156 @@
+/*
+ * 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.FileOutputStream;
+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;
+import org.codehaus.plexus.archiver.ArchiverException;
+import org.codehaus.plexus.archiver.zip.ZipArchiver;
+import org.codehaus.plexus.util.FileUtils;
+
+/**
+ * Attaches the bundle list as a project artifact.
+ *
+ * @goal attach-bundle-list
+ * @phase package
+ * @requiresDependencyResolution test
+ * @description attach the bundle list as a project artifact
+ */
+public class AttachBundleListMojo extends AbstractUsingBundleListMojo {
+
+    /**
+     * @parameter default-value="${project.build.directory}/bundleList.xml"
+     */
+    private File outputFile;
+
+    /**
+     * @parameter default-value="${project.build.directory}/bundleListconfig"
+     */
+    private File configOutputDir;
+
+    /**
+     * The zip archiver.
+     *
+     * @component role="org.codehaus.plexus.archiver.Archiver" roleHint="zip"
+     */
+    private ZipArchiver zipArchiver;
+
+    private final BundleListXpp3Writer writer = new BundleListXpp3Writer();
+
+    @Override
+    protected void executeWithArtifacts() throws MojoExecutionException, MojoFailureException {
+        FileWriter fw = null;
+        try {
+            this.outputFile.getParentFile().mkdirs();
+            fw = new FileWriter(outputFile);
+            writer.write(fw, getInitializedBundleList());
+            projectHelper.attachArtifact(project, AttachPartialBundleListMojo.TYPE, AttachPartialBundleListMojo.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) {
+                }
+            }
+        }
+        this.getLog().info("Attaching bundle list configuration");
+        try {
+            this.attachConfigurations();
+        } catch (final IOException ioe) {
+            throw new MojoExecutionException("Unable to attach configuration.", ioe);
+        } catch (final ArchiverException ioe) {
+            throw new MojoExecutionException("Unable to attach configuration.", ioe);
+        }
+    }
+
+    private boolean checkFile(final File f) {
+        return f != null && f.exists();
+    }
+
+    private void attachConfigurations() throws MojoExecutionException, IOException, ArchiverException {
+        if ( this.ignoreBundleListConfig ) {
+            this.getLog().debug("ignoreBundleListConfig is set to true, therefore not attaching configurations.");
+            return;
+        }
+        // check if we have configurations
+        boolean hasConfigs = this.checkFile(this.getConfigDirectory());
+        hasConfigs |= this.getSlingBootstrap(true) != null;
+        hasConfigs |= this.getSlingBootstrap(false) != null;
+        hasConfigs |= this.getSlingProperties(true) != null;
+        hasConfigs |= this.getSlingProperties(false) != null;
+
+        if ( !hasConfigs ) {
+            this.getLog().debug("No configurations to attach.");
+            return;
+        }
+        // copy configuration, as this project might use different names we have to copy everything!
+        this.configOutputDir.mkdirs();
+        if ( this.getSlingBootstrap(false) != null ) {
+            final File slingDir = new File(this.configOutputDir, "sling");
+            slingDir.mkdirs();
+            FileUtils.fileWrite(new File(slingDir, AttachPartialBundleListMojo.SLING_WEBAPP_BOOTSTRAP).getAbsolutePath(),
+                                "UTF-8", this.getSlingBootstrap(false));
+        }
+        if ( this.getSlingProperties(false) != null ) {
+            final File slingDir = new File(this.configOutputDir, "sling");
+            slingDir.mkdirs();
+            final FileOutputStream fos = new FileOutputStream(new File(slingDir, AttachPartialBundleListMojo.SLING_WEBAPP_PROPS));
+            try {
+                this.getSlingProperties(false).store(fos, null);
+            } finally {
+                try { fos.close(); } catch (final IOException ioe) {}
+            }
+        }
+        if ( this.getSlingBootstrap(true) != null ) {
+            final File slingDir = new File(this.configOutputDir, "sling");
+            slingDir.mkdirs();
+            FileUtils.fileWrite(new File(slingDir, AttachPartialBundleListMojo.SLING_STANDALONE_BOOTSTRAP).getAbsolutePath(),
+                    "UTF-8", this.getSlingBootstrap(true));
+        }
+        if ( this.getSlingProperties(true) != null ) {
+            final File slingDir = new File(this.configOutputDir, "sling");
+            slingDir.mkdirs();
+            final FileOutputStream fos = new FileOutputStream(new File(slingDir, AttachPartialBundleListMojo.SLING_STANDALONE_PROPS));
+            try {
+                this.getSlingProperties(true).store(fos, null);
+            } finally {
+                try { fos.close(); } catch (final IOException ioe) {}
+            }
+        }
+        if ( this.checkFile(this.getConfigDirectory()) ) {
+            final File configDir = new File(this.configOutputDir, "config");
+            configDir.mkdirs();
+            copyDirectory(this.getConfigDirectory(), configDir,
+                    null, FileUtils.getDefaultExcludes());
+        }
+        final File destFile = new File(this.configOutputDir.getParent(), this.configOutputDir.getName() + ".zip");
+        zipArchiver.setDestFile(destFile);
+        zipArchiver.addDirectory(this.configOutputDir);
+        zipArchiver.createArchive();
+
+        projectHelper.attachArtifact(project, AttachPartialBundleListMojo.CONFIG_TYPE,
+                AttachPartialBundleListMojo.CONFIG_CLASSIFIER, destFile);
+    }
+}
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/AttachPartialBundleListMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/AttachPartialBundleListMojo.java
new file mode 100644
index 0000000..2f4caf5
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/AttachPartialBundleListMojo.java
@@ -0,0 +1,191 @@
+/*
+ * 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 static org.apache.sling.maven.projectsupport.BundleListUtils.interpolateProperties;
+import static org.apache.sling.maven.projectsupport.BundleListUtils.readBundleList;
+
+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.BundleList;
+import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.io.xpp3.BundleListXpp3Writer;
+import org.codehaus.plexus.archiver.ArchiverException;
+import org.codehaus.plexus.archiver.zip.ZipArchiver;
+import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+/**
+ * Attaches the bundle list as a project artifact.
+ *
+ * @goal attach-partial-bundle-list
+ * @phase package
+ * @requiresDependencyResolution test
+ * @description attach the partial bundle list as a project artifact
+ */
+public class AttachPartialBundleListMojo extends AbstractBundleListMojo {
+
+    public static final String CONFIG_CLASSIFIER = "bundlelistconfig";
+
+    public static final String CONFIG_TYPE = "zip";
+
+    public static final String CLASSIFIER = "bundlelist";
+
+    public static final String TYPE = "xml";
+
+    public static final String SLING_COMMON_PROPS = "common.properties";
+
+    public static final String SLING_COMMON_BOOTSTRAP = "common.bootstrap.txt";
+
+    public static final String SLING_WEBAPP_PROPS = "webapp.properties";
+
+    public static final String SLING_WEBAPP_BOOTSTRAP = "webapp.bootstrap.txt";
+
+    public static final String SLING_STANDALONE_PROPS = "standalone.properties";
+
+    public static final String SLING_STANDALONE_BOOTSTRAP = "standalone.bootstrap.txt";
+
+    /**
+     * @parameter default-value="${project.build.directory}/bundleListconfig"
+     */
+    private File configOutputDir;
+
+    /**
+     * @parameter default-value="${project.build.directory}/list.xml"
+     */
+    private File bundleListOutput;
+
+    /**
+     * The zip archiver.
+     *
+     * @component role="org.codehaus.plexus.archiver.Archiver" roleHint="zip"
+     */
+    private ZipArchiver zipArchiver;
+
+    public void execute() throws MojoExecutionException, MojoFailureException {
+        final BundleList initializedBundleList;
+        if (bundleListFile.exists()) {
+            try {
+                initializedBundleList = 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);
+            }
+        } else {
+            throw new MojoFailureException(String.format("Bundle list file %s does not exist.", bundleListFile.getAbsolutePath()));
+        }
+
+        interpolateProperties(initializedBundleList, this.project, this.mavenSession);
+
+        final BundleListXpp3Writer writer = new BundleListXpp3Writer();
+        try {
+            this.bundleListOutput.getParentFile().mkdirs();
+            writer.write(new FileWriter(bundleListOutput), initializedBundleList);
+        } catch (IOException e) {
+            throw new MojoExecutionException("Unable to write bundle list", e);
+        }
+
+        // if this project is a partial bundle list, it's the main artifact
+        if ( project.getPackaging().equals(PARTIAL) ) {
+            project.getArtifact().setFile(bundleListOutput);
+        } else {
+            // otherwise attach it as an additional artifact
+            projectHelper.attachArtifact(project, TYPE, CLASSIFIER, bundleListOutput);
+        }
+
+        this.getLog().info("Attaching bundle list configuration");
+        try {
+            this.attachConfigurations();
+        } catch (final IOException ioe) {
+            throw new MojoExecutionException("Unable to attach configuration.", ioe);
+        } catch (final ArchiverException ioe) {
+            throw new MojoExecutionException("Unable to attach configuration.", ioe);
+        }
+    }
+
+    private boolean checkFile(final File f) {
+        return f != null && f.exists();
+    }
+
+    private void attachConfigurations() throws MojoExecutionException, IOException, ArchiverException {
+        if ( this.ignoreBundleListConfig ) {
+            this.getLog().debug("ignoreBundleListConfig is set to true, therefore not attaching configurations.");
+            return;
+        }
+        // check if we have configurations
+        boolean hasConfigs = this.checkFile(this.getConfigDirectory());
+        hasConfigs |= this.checkFile(this.commonSlingBootstrap);
+        hasConfigs |= this.checkFile(this.commonSlingProps);
+        hasConfigs |= this.checkFile(this.webappSlingBootstrap);
+        hasConfigs |= this.checkFile(this.webappSlingProps);
+        hasConfigs |= this.checkFile(this.standaloneSlingBootstrap);
+        hasConfigs |= this.checkFile(this.standaloneSlingProps);
+
+        if ( !hasConfigs ) {
+            this.getLog().debug("No configurations to attach.");
+            return;
+        }
+        // copy configuration, as this project might use different names we have to copy everything!
+        this.configOutputDir.mkdirs();
+        if ( this.checkFile(this.commonSlingBootstrap) ) {
+            final File slingDir = new File(this.configOutputDir, "sling");
+            slingDir.mkdirs();
+            FileUtils.copyFile(this.commonSlingBootstrap, new File(slingDir, SLING_COMMON_BOOTSTRAP));
+        }
+        if ( this.checkFile(this.commonSlingProps) ) {
+            final File slingDir = new File(this.configOutputDir, "sling");
+            slingDir.mkdirs();
+            FileUtils.copyFile(this.commonSlingProps, new File(slingDir, SLING_COMMON_PROPS));
+        }
+        if ( this.checkFile(this.webappSlingBootstrap) ) {
+            final File slingDir = new File(this.configOutputDir, "sling");
+            slingDir.mkdirs();
+            FileUtils.copyFile(this.webappSlingBootstrap, new File(slingDir, SLING_WEBAPP_BOOTSTRAP));
+        }
+        if ( this.checkFile(this.webappSlingProps) ) {
+            final File slingDir = new File(this.configOutputDir, "sling");
+            slingDir.mkdirs();
+            FileUtils.copyFile(this.webappSlingProps, new File(slingDir, SLING_WEBAPP_PROPS));
+        }
+        if ( this.checkFile(this.standaloneSlingBootstrap) ) {
+            final File slingDir = new File(this.configOutputDir, "sling");
+            slingDir.mkdirs();
+            FileUtils.copyFile(this.standaloneSlingBootstrap, new File(slingDir, SLING_STANDALONE_BOOTSTRAP));
+        }
+        if ( this.checkFile(this.standaloneSlingProps) ) {
+            final File slingDir = new File(this.configOutputDir, "sling");
+            slingDir.mkdirs();
+            FileUtils.copyFile(this.standaloneSlingProps, new File(slingDir, SLING_STANDALONE_PROPS));
+        }
+        if ( this.checkFile(this.getConfigDirectory()) ) {
+            final File configDir = new File(this.configOutputDir, "config");
+            configDir.mkdirs();
+            copyDirectory(this.getConfigDirectory(), configDir,
+                    null, FileUtils.getDefaultExcludes());
+        }
+        final File destFile = new File(this.configOutputDir.getParent(), this.configOutputDir.getName() + ".zip");
+        zipArchiver.setDestFile(destFile);
+        zipArchiver.addDirectory(this.configOutputDir);
+        zipArchiver.createArchive();
+
+        projectHelper.attachArtifact(project, CONFIG_TYPE, CONFIG_CLASSIFIER, destFile);
+    }
+}
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/BundleListUtils.java b/src/main/java/org/apache/sling/maven/projectsupport/BundleListUtils.java
new file mode 100644
index 0000000..4b96313
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/BundleListUtils.java
@@ -0,0 +1,154 @@
+/*
+ * 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.FileInputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Properties;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.settings.Settings;
+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.interpolation.InterpolationException;
+import org.codehaus.plexus.interpolation.Interpolator;
+import org.codehaus.plexus.interpolation.PrefixedObjectValueSource;
+import org.codehaus.plexus.interpolation.PropertiesBasedValueSource;
+import org.codehaus.plexus.interpolation.StringSearchInterpolator;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+/**
+ * Miscellaneous helper methods for working with bundle lists.
+ */
+public class BundleListUtils {
+    
+    private BundleListUtils() {}
+    
+    /**
+     * Initialize the artifact definitions using defaults inside the plugin JAR.
+     *
+     * @throws IOException if the default properties can't be read
+     * @throws XmlPullParserException
+     * @throws MojoExecutionException
+     */
+    public static final void initArtifactDefinitions(ClassLoader classLoader, ArtifactDefinitionsCallback callback) throws IOException {
+        Properties dependencies = new Properties();
+        dependencies.load(classLoader.getResourceAsStream(
+                "org/apache/sling/maven/projectsupport/dependencies.properties"));
+
+        callback.initArtifactDefinitions(dependencies);
+    }
+    
+    public static boolean isCurrentArtifact(MavenProject project, ArtifactDefinition def) {
+        return (def.getGroupId().equals(project.getGroupId()) && def.getArtifactId().equals(project.getArtifactId()));
+    }
+    
+    public static BundleList readBundleList(File file) throws IOException, XmlPullParserException {
+        BundleListXpp3Reader reader = new BundleListXpp3Reader();
+        FileInputStream fis = new FileInputStream(file);
+        try {
+            return reader.read(fis);
+        } finally {
+            fis.close();
+        }
+    }
+    
+    public static int nodeValue(Xpp3Dom config, String name, int defaultValue) {
+        Xpp3Dom node = config.getChild(name);
+        if (node != null) {
+            return Integer.parseInt(node.getValue());
+        } else {
+            return defaultValue;
+        }
+    }
+    
+    public static boolean nodeValue(Xpp3Dom config, String name, boolean defaultValue) {
+        Xpp3Dom node = config.getChild(name);
+        if (node != null) {
+            return Boolean.parseBoolean(node.getValue());
+        } else {
+            return defaultValue;
+        }
+    }
+
+    public static String nodeValue(Xpp3Dom config, String name, String defaultValue) {
+        Xpp3Dom node = config.getChild(name);
+        if (node != null) {
+            return node.getValue();
+        } else {
+            return defaultValue;
+        }
+    }
+    
+    public static void interpolateProperties(BundleList bundleList, MavenProject project, MavenSession mavenSession) throws MojoExecutionException {
+        Interpolator interpolator = createInterpolator(project, mavenSession);
+        for (final StartLevel sl : bundleList.getStartLevels()) {
+            for (final Bundle bndl : sl.getBundles()) {
+                try {
+                    bndl.setArtifactId(interpolator.interpolate(bndl.getArtifactId()));
+                    bndl.setGroupId(interpolator.interpolate(bndl.getGroupId()));
+                    bndl.setVersion(interpolator.interpolate(bndl.getVersion()));
+                    bndl.setClassifier(interpolator.interpolate(bndl.getClassifier()));
+                    bndl.setType(interpolator.interpolate(bndl.getType()));
+                } catch (InterpolationException e) {
+                    throw new MojoExecutionException("Unable to interpolate properties for bundle " + bndl.toString(), e);
+                }
+            }
+        }
+
+    }
+    
+    public static Interpolator createInterpolator(MavenProject project, MavenSession mavenSession) {
+        StringSearchInterpolator interpolator = new StringSearchInterpolator();
+
+        final Properties props = new Properties();
+        props.putAll(project.getProperties());
+        props.putAll(mavenSession.getSystemProperties());
+        props.putAll(mavenSession.getUserProperties());
+        
+        interpolator.addValueSource(new PropertiesBasedValueSource(props));
+
+        // add ${project.foo}
+        interpolator.addValueSource(new PrefixedObjectValueSource(Arrays.asList("project", "pom"), project, true));
+
+        // add ${session.foo}
+        interpolator.addValueSource(new PrefixedObjectValueSource("session", mavenSession));
+
+        // add ${settings.foo}
+        final Settings settings = mavenSession.getSettings();
+        if (settings != null) {
+            interpolator.addValueSource(new PrefixedObjectValueSource("settings", settings));
+        }
+
+        return interpolator;
+    }
+
+    /**
+     * Callback interface for use with initArtifactDefinitions.
+     */
+    public static interface ArtifactDefinitionsCallback {
+        void initArtifactDefinitions(Properties dependencies);
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/CheckBundleListForSnapshotsMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/CheckBundleListForSnapshotsMojo.java
new file mode 100644
index 0000000..59e92cf
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/CheckBundleListForSnapshotsMojo.java
@@ -0,0 +1,74 @@
+/*
+ * 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.util.ArrayList;
+import java.util.List;
+
+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;
+
+/**
+ * Validate that the bundle list file (if it exists) does not contain references
+ * to SNAPSHOT versions.
+ *
+ * @goal check-bundle-list-for-snapshots
+ * @requiresDependencyResolution test
+ *
+ */
+public class CheckBundleListForSnapshotsMojo extends AbstractUsingBundleListMojo {
+
+    /**
+     * 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 {
+        List<Bundle> snapshots = new ArrayList<Bundle>();
+        BundleList bundleList = getInitializedBundleList();
+        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.");
+            }
+        }
+    }
+
+    private boolean isSnapshot(Bundle bundle) {
+        return bundle.getVersion().endsWith("SNAPSHOT");
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/ControlListener.java b/src/main/java/org/apache/sling/maven/projectsupport/ControlListener.java
new file mode 100644
index 0000000..3e3dba5
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/ControlListener.java
@@ -0,0 +1,212 @@
+/*
+ * 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.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.ConnectException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.UnknownHostException;
+
+import org.apache.maven.plugin.logging.Log;
+
+/**
+ * This class is adapted from org.apache.sling.launchpad.app.ControlListener.
+ */
+class ControlListener implements Runnable {
+    // command sent by the client to cause Sling to shutdown
+    static final String COMMAND_STOP = "stop";
+
+    // command sent by the client to check for the status of the server
+    static final String COMMAND_STATUS = "status";
+
+    // the response sent by the server if the command executed successfully
+    private static final String RESPONSE_OK = "OK";
+
+    // The default port to listen on and to connect to
+    private static final int DEFAULT_LISTEN_PORT = 63000;
+
+    /** The mojo */
+    private AbstractLaunchpadStartingMojo mojo;
+
+    /** The log object */
+    private final Log log;
+
+    /** The socket address used for control communication */
+    private final SocketAddress socketAddress;
+
+    ControlListener(AbstractLaunchpadStartingMojo mojo, Log log, String host, int port) {
+        this.mojo = mojo;
+        this.log = log;
+        this.socketAddress = getSocketAddress(host, port);
+    }
+
+    /**
+     * Implements the server side of the control connection starting a thread
+     * listening on the host and port configured on setup of this instance.
+     */
+    void listen() {
+        if (socketAddress != null) {
+            Thread listener = new Thread(this);
+            listener.setDaemon(true);
+            listener.setName("Sling Control Listener@" + socketAddress);
+            listener.start();
+        } else {
+            log.info("No socket address to listen to");
+        }
+    }
+
+    /**
+     * Implements the client side of the control connection sending the command
+     * to shutdown Sling.
+     */
+    void shutdownServer() {
+        sendCommand(COMMAND_STOP);
+    }
+
+    /**
+     * Implements the client side of the control connection sending the command
+     * to check whether Sling is active.
+     */
+    void statusServer() {
+        sendCommand(COMMAND_STATUS);
+    }
+
+    // ---------- Runnable interface
+
+    /**
+     * Implements the server thread receiving commands from clients and acting
+     * upon them.
+     */
+    public void run() {
+        ServerSocket server = null;
+        try {
+            server = new ServerSocket();
+            server.bind(socketAddress);
+            log.info("Sling Control Server started on " + socketAddress.toString());
+        } catch (IOException ioe) {
+            log.error("Failed to start Sling Control Server", ioe);
+            return;
+        }
+
+        try {
+            while (true) {
+
+                Socket s = server.accept();
+                try {
+                    String command = readLine(s);
+                    log.info(s.getRemoteSocketAddress() + ">" + command);
+
+                    if (COMMAND_STOP.equals(command)) {
+                        if (mojo != null) {
+                            mojo.stopSling();
+                        }
+                        writeLine(s, RESPONSE_OK);
+
+                        log.info("Sling shut down, stopping Sling.");
+                        mojo.stopSling();
+
+                    } else if (COMMAND_STATUS.equals(command)) {
+                        writeLine(s, RESPONSE_OK);
+
+                    } else {
+                        writeLine(s, "ERR:" + command);
+
+                    }
+                } finally {
+                    try {
+                        s.close();
+                    } catch (IOException ignore) {
+                    }
+                }
+            }
+        } catch (IOException ioe) {
+            log.error("Failure reading from client", ioe);
+        } finally {
+            try {
+                server.close();
+            } catch (IOException ignore) {
+            }
+        }
+    }
+
+    // ---------- socket support
+
+    private SocketAddress getSocketAddress(String host, int port) {
+        try {
+            if (port == -1) {
+                port = DEFAULT_LISTEN_PORT;
+            }
+
+            if (host != null) {
+                return new InetSocketAddress(host, port);
+            } else {
+
+                return new InetSocketAddress(InetAddress.getLocalHost(), port);
+            }
+        } catch (UnknownHostException uhe) {
+            log.error("Unknown host in '" + host + "': " + uhe.getMessage(), null);
+        }
+
+        return null;
+    }
+
+    private void sendCommand(String command) {
+        if (socketAddress != null) {
+            Socket socket = null;
+            try {
+                socket = new Socket();
+                socket.connect(socketAddress);
+                writeLine(socket, command);
+                String result = readLine(socket);
+                log.info("Sent '" + command + "' to " + socketAddress + ": " + result, null);
+            } catch (ConnectException ce) {
+                log.info("No Sling running at " + socketAddress, null);
+            } catch (IOException ioe) {
+                log.error("Failed sending '" + command + "' to " + socketAddress, ioe);
+            } finally {
+                if (socket != null) {
+                    try {
+                        socket.close();
+                    } catch (IOException ignore) {
+                    }
+                }
+            }
+        } else {
+            log.info("No socket address to send '" + command + "' to", null);
+        }
+    }
+
+    private String readLine(Socket socket) throws IOException {
+        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
+        return br.readLine();
+    }
+
+    private void writeLine(Socket socket, String line) throws IOException {
+        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
+        bw.write(line);
+        bw.write("\r\n");
+        bw.flush();
+    }
+}
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/CreateBundleJarMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/CreateBundleJarMojo.java
new file mode 100644
index 0000000..c46da9b
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/CreateBundleJarMojo.java
@@ -0,0 +1,187 @@
+/*
+ * 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.model.Resource;
+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.archiver.ArchiverException;
+import org.codehaus.plexus.archiver.jar.JarArchiver;
+import org.codehaus.plexus.util.DirectoryScanner;
+
+/**
+ * Create and attach a JAR file containing the resolved artifacts from the
+ * bundle list.
+ *
+ * @goal create-bundle-jar
+ * @requiresDependencyResolution test
+ * @phase package
+ *
+ */
+public class CreateBundleJarMojo extends AbstractLaunchpadFrameworkMojo {
+
+    /**
+     * The list of resources we want to add to the bundle JAR file.
+     *
+     * @parameter
+     */
+    private Resource[] resources;
+
+    /**
+     * The output directory.
+     *
+     * @parameter default-value="${project.build.directory}"
+     */
+    private File outputDirectory;
+
+    /**
+     * Name of the generated JAR.
+     *
+     * @parameter default-value="${project.artifactId}-${project.version}"
+     * @required
+     */
+    private String jarName;
+
+    /**
+     * The Jar archiver.
+     *
+     * @component role="org.codehaus.plexus.archiver.Archiver" roleHint="jar"
+     */
+    private JarArchiver jarArchiver;
+
+    private static final String CLASSIFIER = "bundles";
+
+    public static final String[] DEFAULT_INCLUDES = { "**/**" };
+
+    private void addBundles() throws MojoExecutionException {
+        BundleList bundles = getInitializedBundleList();
+
+        for (StartLevel level : bundles.getStartLevels()) {
+            for (Bundle bundle : level.getBundles()) {
+                Artifact artifact = getArtifact(new ArtifactDefinition(bundle,
+                        level.getStartLevel()));
+                final String destFileName = getPathForArtifact(level.getStartLevel(), bundle.getRunModes(), artifact.getFile().getName());
+                try {
+                    jarArchiver.addFile(artifact.getFile(), destFileName);
+                } catch (ArchiverException e) {
+                    throw new MojoExecutionException(
+                            "Unable to add file to bundle jar file: "
+                                    + artifact.getFile().getAbsolutePath(), e);
+                }
+            }
+        }
+    }
+
+    private void addResources(Resource resource) throws MojoExecutionException {
+        getLog().info(
+                String.format("Adding resources [%s] to [%s]", resource
+                        .getDirectory(), resource.getTargetPath()));
+        String[] fileNames = getFilesToCopy(resource);
+        for (int i = 0; i < fileNames.length; i++) {
+            String targetFileName = fileNames[i];
+            if (resource.getTargetPath() != null) {
+                targetFileName = resource.getTargetPath() + File.separator
+                        + targetFileName;
+            }
+
+            try {
+                jarArchiver.addFile(new File(resource.getDirectory(),
+                        fileNames[i]), targetFileName);
+            } catch (ArchiverException e) {
+                throw new MojoExecutionException(
+                        "Unable to add resources to JAR file", e);
+            }
+
+        }
+    }
+
+    private File createJARFile() throws MojoExecutionException {
+        File jarFile = new File(outputDirectory, jarName + "-" + CLASSIFIER
+                + "." + JAR);
+        jarArchiver.setDestFile(jarFile);
+
+        addBundles();
+        addResources();
+
+        try {
+            jarArchiver.createArchive();
+        } catch (ArchiverException e) {
+            throw new MojoExecutionException(
+                    "Unable to create bundle jar file", e);
+        } catch (IOException e) {
+            throw new MojoExecutionException(
+                    "Unable to create bundle jar file", e);
+        }
+
+        return jarFile;
+    }
+
+    private void addResources() throws MojoExecutionException {
+        if (resources != null) {
+            for (Resource resource : resources) {
+                if (!(new File(resource.getDirectory())).isAbsolute()) {
+                    resource.setDirectory(project.getBasedir() + File.separator
+                            + resource.getDirectory());
+                }
+                addResources(resource);
+            }
+        }
+    }
+
+    @Override
+    protected void executeWithArtifacts() throws MojoExecutionException,
+            MojoFailureException {
+        File jarFile = createJARFile();
+        projectHelper.attachArtifact(project, JAR, CLASSIFIER, jarFile);
+    }
+
+    /**
+     * Returns a list of filenames that should be copied over to the destination
+     * directory.
+     *
+     * @param resource
+     *            the resource to be scanned
+     * @return the array of filenames, relative to the sourceDir
+     */
+    private static String[] getFilesToCopy(Resource resource) {
+        DirectoryScanner scanner = new DirectoryScanner();
+        scanner.setBasedir(resource.getDirectory());
+        if (resource.getIncludes() != null && !resource.getIncludes().isEmpty()) {
+            scanner.setIncludes(resource.getIncludes().toArray(
+                    new String[resource.getIncludes().size()]));
+        } else {
+            scanner.setIncludes(DEFAULT_INCLUDES);
+        }
+        if (resource.getExcludes() != null && !resource.getExcludes().isEmpty()) {
+            scanner.setExcludes(resource.getExcludes().toArray(
+                    new String[resource.getExcludes().size()]));
+        }
+
+        scanner.addDefaultExcludes();
+
+        scanner.scan();
+
+        return scanner.getIncludedFiles();
+    }
+}
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/CreateKarafFeatureDescriptorMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/CreateKarafFeatureDescriptorMojo.java
new file mode 100644
index 0000000..504d9cd
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/CreateKarafFeatureDescriptorMojo.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.sling.maven.projectsupport;
+
+import java.io.File;
+import java.io.FileOutputStream;
+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.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.jdom.Document;
+import org.jdom.Element;
+import org.jdom.output.Format;
+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
+ * @requiresDependencyResolution test
+ */
+public class CreateKarafFeatureDescriptorMojo extends AbstractUsingBundleListMojo {
+
+    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 = getInitializedBundleList();
+        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/CreatePaxRunnerBundleProvisionFileMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/CreatePaxRunnerBundleProvisionFileMojo.java
new file mode 100644
index 0000000..9e98997
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/CreatePaxRunnerBundleProvisionFileMojo.java
@@ -0,0 +1,77 @@
+/*
+ * 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.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.Bundle;
+import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.BundleList;
+import org.apache.sling.maven.projectsupport.bundlelist.v1_0_0.StartLevel;
+
+/**
+ * Create and attach a Pax Runner bundle provision file.
+ * 
+ * @goal create-paxrunner-provision-file
+ * @phase package
+ * @description create a Pax Runner bundle provision file
+ * @requiresDependencyResolution test
+ */
+public class CreatePaxRunnerBundleProvisionFileMojo extends AbstractUsingBundleListMojo {
+
+    private static final String CLASSIFIER = "bundles";
+
+    private static final String TYPE = "pax";
+
+    /**
+     * The output directory.
+     * 
+     * @parameter default-value="${project.build.directory}/bundles-pax"
+     */
+    private File outputFile;
+
+    @Override
+    protected void executeWithArtifacts() throws MojoExecutionException, MojoFailureException {
+        FileWriter out = null;
+        try {
+            out = new FileWriter(outputFile);
+
+            BundleList bundleList = getInitializedBundleList();
+            for (StartLevel level : bundleList.getStartLevels()) {
+                for (Bundle bundle : level.getBundles()) {
+                    String line = String.format("mvn:%s/%s/%s@%d\n", bundle.getGroupId(), bundle.getArtifactId(),
+                            bundle.getVersion(), level.getStartLevel());
+                    out.write(line);
+                }
+            }
+
+            projectHelper.attachArtifact(project, TYPE, CLASSIFIER, outputFile);
+        } catch (IOException e) {
+            throw new MojoExecutionException("Unable to write " + outputFile.getName(), e);
+        } finally {
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/DisplayBundleUpdatesMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/DisplayBundleUpdatesMojo.java
new file mode 100644
index 0000000..7f11573
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/DisplayBundleUpdatesMojo.java
@@ -0,0 +1,254 @@
+/*
+ * 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.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.maven.artifact.ArtifactUtils;
+import org.apache.maven.artifact.manager.WagonManager;
+import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.versioning.ArtifactVersion;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.project.path.PathTranslator;
+import org.apache.maven.settings.Settings;
+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.mojo.versions.api.ArtifactVersions;
+import org.codehaus.mojo.versions.api.DefaultVersionsHelper;
+import org.codehaus.mojo.versions.api.UpdateScope;
+import org.codehaus.mojo.versions.api.VersionsHelper;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+/**
+ * Displays all bundles that have newer versions available. Highly based upon the
+ * display-dependency-updates goal from the Versions plugin.
+ *
+ * @since 2.0.8
+ * @goal display-bundle-updates
+ *
+ */
+public class DisplayBundleUpdatesMojo extends AbstractMojo {
+
+    /**
+     * The width to pad info messages.
+     */
+    private static final int INFO_PAD_SIZE = 72;
+
+    /**
+     * @component
+     */
+    private org.apache.maven.artifact.factory.ArtifactFactory artifactFactory;
+
+    /**
+     * The artifact metadata source to use.
+     *
+     * @component
+     * @required
+     * @readonly
+     */
+    private ArtifactMetadataSource artifactMetadataSource;
+
+    /**
+     * @parameter expression="${project.remoteArtifactRepositories}"
+     * @readonly
+     */
+    private List<ArtifactRepository> remoteArtifactRepositories;
+
+    /**
+     * @parameter expression="${project.pluginArtifactRepositories}"
+     * @readonly
+     */
+    private List<ArtifactRepository> remotePluginRepositories;
+
+    /**
+     * @parameter expression="${localRepository}"
+     * @readonly
+     */
+    private ArtifactRepository localRepository;
+
+    /**
+     * @component
+     */
+    private WagonManager wagonManager;
+
+    /**
+     * @parameter expression="${settings}"
+     * @readonly
+     */
+    private Settings settings;
+
+    /**
+     * settings.xml's server id for the URL. This is used when wagon needs extra
+     * authentication information.
+     *
+     * @parameter expression="${maven.version.rules.serverId}"
+     *            default-value="serverId";
+     */
+    private String serverId;
+
+    /**
+     * The Wagon URI of a ruleSet file containing the rules that control how to
+     * compare version numbers.
+     *
+     * @parameter expression="${maven.version.rules}"
+     */
+    private String rulesUri;
+
+    /**
+     * The Maven Session.
+     *
+     * @parameter expression="${session}"
+     * @required
+     * @readonly
+     */
+    private MavenSession session;
+
+    /**
+     * @component
+     */
+    private PathTranslator pathTranslator;
+
+    /**
+     * @parameter default-value="${basedir}/src/main/bundles/list.xml"
+     */
+    private File bundleListFile;
+
+    /**
+     * Whether to allow snapshots when searching for the latest version of an
+     * artifact.
+     *
+     * @parameter expression="${allowSnapshots}" default-value="false"
+     */
+    private boolean allowSnapshots;
+
+    /**
+     * Our versions helper.
+     */
+    private VersionsHelper helper;
+
+    @SuppressWarnings("unchecked")
+    public void execute() throws MojoExecutionException, MojoFailureException {
+        try {
+            BundleList bundleList = readBundleList(bundleListFile);
+
+            Set<Dependency> bundlesAsDependencies = new HashSet<Dependency>();
+
+            for (StartLevel startLevel : bundleList.getStartLevels()) {
+                for (Bundle bundle : startLevel.getBundles()) {
+                    bundlesAsDependencies.add(asDependency(bundle));
+                }
+            }
+
+            logUpdates(getHelper().lookupDependenciesUpdates(bundlesAsDependencies, false));
+        } catch (Exception e) {
+            throw new MojoExecutionException("Unable to read bundle list.", e);
+        }
+
+    }
+
+    private Dependency asDependency(Bundle bundle) {
+        Dependency dep = new Dependency();
+        dep.setGroupId(bundle.getGroupId());
+        dep.setArtifactId(bundle.getArtifactId());
+        dep.setClassifier(bundle.getClassifier());
+        dep.setVersion(bundle.getVersion());
+        dep.setType(bundle.getType());
+
+        return dep;
+    }
+
+    private VersionsHelper getHelper() throws MojoExecutionException {
+        if (helper == null) {
+            helper = new DefaultVersionsHelper(artifactFactory, artifactMetadataSource, remoteArtifactRepositories,
+                    remotePluginRepositories, localRepository, wagonManager, settings, serverId, rulesUri, getLog(),
+                    session, pathTranslator);
+        }
+        return helper;
+    }
+
+    private void logUpdates(Map<Dependency, ArtifactVersions> updates) {
+        List<String> withUpdates = new ArrayList<String>();
+        List<String> usingCurrent = new ArrayList<String>();
+        for (ArtifactVersions versions : updates.values()) {
+            String left = "  " + ArtifactUtils.versionlessKey(versions.getArtifact()) + " ";
+            final String current = versions.isCurrentVersionDefined() ? versions.getCurrentVersion().toString()
+                    : versions.getArtifact().getVersionRange().toString();
+            ArtifactVersion latest = versions.getNewestUpdate(UpdateScope.ANY, Boolean.TRUE.equals(allowSnapshots));
+            if (latest != null && !versions.isCurrentVersionDefined()) {
+                if (versions.getArtifact().getVersionRange().containsVersion(latest)) {
+                    latest = null;
+                }
+            }
+            String right = " " + (latest == null ? current : current + " -> " + latest.toString());
+            List<String> t = latest == null ? usingCurrent : withUpdates;
+            if (right.length() + left.length() + 3 > INFO_PAD_SIZE) {
+                t.add(left + "...");
+                t.add(StringUtils.leftPad(right, INFO_PAD_SIZE));
+
+            } else {
+                t.add(StringUtils.rightPad(left, INFO_PAD_SIZE - right.length(), ".") + right);
+            }
+        }
+
+        if (usingCurrent.isEmpty() && !withUpdates.isEmpty()) {
+            getLog().info("No bundles are using the newest version.");
+            getLog().info("");
+        } else if (!usingCurrent.isEmpty()) {
+            getLog().info("The following bundles are using the newest version:");
+            for (String str : usingCurrent) {
+                getLog().info(str);
+            }
+            getLog().info("");
+        }
+        if (withUpdates.isEmpty() && !usingCurrent.isEmpty()) {
+            getLog().info("No bundles have newer versions.");
+            getLog().info("");
+        } else if (!withUpdates.isEmpty()) {
+            getLog().info("The following bundles have newer versions:");
+            for (String str : withUpdates) {
+                getLog().info(str);
+            }
+            getLog().info("");
+        }
+    }
+
+    private BundleList readBundleList(File file) throws IOException, XmlPullParserException {
+        BundleListXpp3Reader reader = new BundleListXpp3Reader();
+        FileInputStream fis = new FileInputStream(file);
+        try {
+            return reader.read(fis);
+        } finally {
+            fis.close();
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/LaunchpadPluginLifecycleParticipant.java b/src/main/java/org/apache/sling/maven/projectsupport/LaunchpadPluginLifecycleParticipant.java
new file mode 100644
index 0000000..57dd147
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/LaunchpadPluginLifecycleParticipant.java
@@ -0,0 +1,232 @@
+/*
+ * 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 static org.apache.sling.maven.projectsupport.BundleListUtils.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.maven.AbstractMavenLifecycleParticipant;
+import org.apache.maven.MavenExecutionException;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.model.PluginExecution;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+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.component.annotations.Component;
+import org.codehaus.plexus.component.annotations.Requirement;
+import org.codehaus.plexus.logging.Logger;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
+
+/**
+ * Maven lifecycle participant which adds the default bundle list, the
+ * jar web support bundle, and the contents of any local bundle list.
+ */
+@Component(role = AbstractMavenLifecycleParticipant.class)
+public class LaunchpadPluginLifecycleParticipant extends AbstractMavenLifecycleParticipant {
+
+    private static final String PLUGIN_ID = "maven-launchpad-plugin";
+    
+    private static final String PROVIDED = "provided";
+
+    @Requirement
+    private Logger log;
+
+    @Override
+    public void afterProjectsRead(MavenSession session) throws MavenExecutionException {
+        try {
+            Map<String, MavenProject> projectMap = new HashMap<String, MavenProject>();
+            for (MavenProject project : session.getProjects()) {
+                projectMap.put(project.getGroupId() + ":" + project.getArtifactId() + ":" + project.getVersion(),
+                        project);
+            }
+
+            for (MavenProject project : session.getProjects()) {
+                for (Plugin plugin : project.getBuild().getPlugins()) {
+                    if (plugin.getArtifactId().equals(PLUGIN_ID)) {
+                        BundleListDependencyAdder performer = new BundleListDependencyAdder(session, project, plugin);
+                        performer.addDependencies();
+                    }
+                }
+            }
+        } catch (Exception e) {
+            throw new MavenExecutionException("Unable to determine launchpad plugin-based dependencies", e);
+        }
+        super.afterProjectsRead(session);
+    }
+
+    private class BundleListDependencyAdder {
+
+        private final MavenSession session;
+        private final MavenProject project;
+        private final Plugin plugin;
+        private final List<ArtifactDefinition> additionalBundles;
+
+        private ArtifactDefinition defaultBundleList;
+        private boolean includeDefaultBundles;
+        private ArtifactDefinition jarWebSupport;
+        private File bundleListFile;
+
+        public BundleListDependencyAdder(MavenSession session, MavenProject project, Plugin plugin) {
+            this.session = session;
+            this.project = project;
+            this.plugin = plugin;
+            this.additionalBundles = new ArrayList<ArtifactDefinition>();
+        }
+
+        void addDependencies() throws Exception {
+            readConfiguration();
+            
+            addBundleListDependencies();
+
+            if (hasPreparePackageExecution()) {
+                if (includeDefaultBundles && !isCurrentArtifact(project, defaultBundleList)) {
+                    log.debug(String.format("adding default bundle list (%s) to dependencies of project %s", defaultBundleList, project));
+                    project.getDependencies().add(defaultBundleList.toDependency(PROVIDED));
+                }
+
+                if (hasJarPackagingExecution()) {
+                    log.debug(String.format("adding jar web support (%s) to dependencies of project %s", jarWebSupport, project));
+                    project.getDependencies().add(jarWebSupport.toDependency(PROVIDED));
+                }
+            }
+        }
+
+        private void addBundleListDependencies() throws IOException, XmlPullParserException, MojoExecutionException {
+            BundleList bundleList;
+            
+            if (bundleListFile.exists()) {
+                bundleList = readBundleList(bundleListFile);
+            } else {
+                bundleList = new BundleList();
+            }
+            
+            if (additionalBundles != null) {
+                for (ArtifactDefinition def : additionalBundles) {
+                    bundleList.add(def.toBundle());
+                }
+            }
+            
+            interpolateProperties(bundleList, project, session);
+            
+            for (StartLevel startLevel : bundleList.getStartLevels()) {
+                for (Bundle bundle : startLevel.getBundles()) {
+                    log.debug(String.format("adding bundle (%s) from bundle list to dependencies of project %s", bundle, project));
+                    project.getDependencies().add(ArtifactDefinition.toDependency(bundle, PROVIDED));
+                }
+            }
+        }
+
+        private void readConfiguration() throws IOException {
+            Xpp3Dom configuration = (Xpp3Dom) plugin.getConfiguration();
+            defaultBundleList = null;
+            jarWebSupport = null;
+            includeDefaultBundles = true;
+            bundleListFile = new File(project.getBasedir(), "src/main/bundles/list.xml");
+            if (configuration != null) {
+                includeDefaultBundles = nodeValue(configuration, "includeDefaultBundles", true);
+                Xpp3Dom defaultBundleListConfig = configuration.getChild("defaultBundleList");
+                if (defaultBundleListConfig != null) {
+                    defaultBundleList = new ArtifactDefinition(defaultBundleListConfig);
+                }
+                Xpp3Dom jarWebSupportConfig = configuration.getChild("jarWebSupport");
+                if (jarWebSupportConfig != null) {
+                    jarWebSupport = new ArtifactDefinition(jarWebSupportConfig);
+                }
+                Xpp3Dom bundleListFileConfig = configuration.getChild("bundleListFile");
+                if (bundleListFileConfig != null) {
+                    bundleListFile = new File(project.getBasedir(), bundleListFileConfig.getValue());
+                }
+                
+                configureAdditionalBundles(configuration);
+            }
+            
+            for (PluginExecution execution : plugin.getExecutions()) {
+                Xpp3Dom executionConfiguration = (Xpp3Dom) execution.getConfiguration();
+                if (executionConfiguration != null) {
+                    configureAdditionalBundles(executionConfiguration);
+                }
+            }
+
+            initArtifactDefinitions(getClass().getClassLoader(), new ArtifactDefinitionsCallback() {
+
+                public void initArtifactDefinitions(Properties dependencies) {
+                    if (defaultBundleList == null) {
+                        defaultBundleList = new ArtifactDefinition();
+                    }
+                    defaultBundleList.initDefaults(dependencies.getProperty("defaultBundleList"));
+
+                    if (jarWebSupport == null) {
+                        jarWebSupport = new ArtifactDefinition();
+                    }
+                    jarWebSupport.initDefaults(dependencies.getProperty("jarWebSupport"));
+                }
+            });
+        }
+
+        private void configureAdditionalBundles(Xpp3Dom configuration) {
+            Xpp3Dom additionalBundlesConfig = configuration.getChild("additionalBundles");
+            if (additionalBundlesConfig != null) {
+                Xpp3Dom[] bundleConfigs = additionalBundlesConfig.getChildren("bundle");
+                if (bundleConfigs != null) {
+                    for (Xpp3Dom bundleConfig : bundleConfigs) {
+                        additionalBundles.add(new ArtifactDefinition(bundleConfig));
+                    }
+                }
+            }
+        }
+
+        private boolean hasJarPackagingExecution() {
+            if (AbstractUsingBundleListMojo.JAR.equals(project.getPackaging())) {
+                return true;
+            } else {
+                for (PluginExecution execution : plugin.getExecutions()) {
+                    if (execution.getGoals().contains("prepare-package")) {
+                        Xpp3Dom executionConfig = (Xpp3Dom) execution.getConfiguration();
+                        if (executionConfig != null) {
+                            Xpp3Dom packagingConfig = executionConfig.getChild("packaging");
+                            if (packagingConfig != null
+                                    && AbstractUsingBundleListMojo.JAR.equals(packagingConfig.getValue())) {
+                                return true;
+                            }
+                        }
+                    }
+                }
+                return false;
+            }
+        }
+
+        private boolean hasPreparePackageExecution() {
+            for (PluginExecution execution : plugin.getExecutions()) {
+                if (execution.getGoals().contains("prepare-package")) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/OutputBundleListMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/OutputBundleListMojo.java
new file mode 100644
index 0000000..31d0523
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/OutputBundleListMojo.java
@@ -0,0 +1,45 @@
+/*
+ * 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.IOException;
+import java.io.OutputStreamWriter;
+
+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;
+
+/**
+ * Output the bundle list back to the console.
+ * 
+ * @goal output-bundle-list
+ * @requiresDependencyResolution test
+ * 
+ */
+public class OutputBundleListMojo extends AbstractUsingBundleListMojo {
+
+    @Override
+    protected void executeWithArtifacts() throws MojoExecutionException, MojoFailureException {
+        BundleListXpp3Writer writer = new BundleListXpp3Writer();
+        try {
+            writer.write(new OutputStreamWriter(System.out), getInitializedBundleList());
+        } catch (IOException e) {
+            throw new MojoExecutionException("Unable to write bundle list", 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
new file mode 100644
index 0000000..a221434
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/PreparePackageMojo.java
@@ -0,0 +1,352 @@
+/*
+ * 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.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Properties;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+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.jar.JarArchiver;
+import org.codehaus.plexus.archiver.manager.ArchiverManager;
+import org.codehaus.plexus.archiver.manager.NoSuchArchiverException;
+import org.codehaus.plexus.util.FileUtils;
+
+/**
+ * Initialize a Sling application project by extracting bundles into the correct
+ * locations.
+ *
+ * @goal prepare-package
+ * @requiresDependencyResolution test
+ * @phase process-sources
+ * @description initialize a Sling application project
+ */
+public class PreparePackageMojo extends AbstractLaunchpadFrameworkMojo {
+
+    /**
+     * The output directory for the default bundles in a WAR-packaged project,
+     * the base JAR (in the subdirectory named in the baseDestination
+     * parameter), and any additional bundles.
+     *
+     * @parameter default-value="${project.build.directory}/launchpad-bundles"
+     */
+    private File warOutputDirectory;
+
+    /**
+     * The project's packaging type.
+     *
+     * @parameter expression="${project.packaging}"
+     */
+    private String packaging;
+
+    /**
+     * The definition of the base JAR.
+     *
+     * @parameter
+     */
+    private ArtifactDefinition base;
+
+    /**
+     * The definition of the package to be included to provide web support for
+     * JAR-packaged projects (i.e. pax-web).
+     *
+     * @parameter
+     */
+    private ArtifactDefinition jarWebSupport;
+
+    /**
+     * The project's build output directory (i.e. target/classes).
+     *
+     * @parameter expression="${project.build.outputDirectory}"
+     * @readonly
+     */
+    private File buildOutputDirectory;
+
+    /**
+     * The temp directory (i.e. target/maven-launchpad-plugintmp).
+     *
+     * @parameter expression="${project.build.directory}/maven-launchpad-plugintmp"
+     * @readonly
+     */
+    private File tempDirectory;
+
+    /**
+     * To look up Archiver/UnArchiver implementations
+     *
+     * @component
+     */
+    private ArchiverManager archiverManager;
+
+    /**
+     * The Jar archiver.
+     *
+     * @component role="org.codehaus.plexus.archiver.Archiver" roleHint="jar"
+     */
+    private JarArchiver jarArchiver;
+
+    @Override
+    public void executeWithArtifacts() throws MojoExecutionException, MojoFailureException {
+        copyBaseArtifact();
+        copyBundles(getInitializedBundleList(), getOutputDirectory());
+        copyConfigurationFiles();
+        if (JAR.equals(packaging)) {
+            unpackBaseArtifact();
+        }
+    }
+
+    @Override
+    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"));
+    }
+
+    /**
+     * Add the JAR Web Support bundle to the bundle list.
+     */
+    @Override
+    protected void initBundleList(BundleList bundleList) {
+        if (packaging.equals(JAR)) {
+            bundleList.add(jarWebSupport.toBundle());
+        }
+    }
+
+    /**
+     * Patch the sling properties
+     */
+    private void patchSlingProperties(final File dest, final Properties additionalProps)
+    throws MojoExecutionException {
+        final File origSlingProps = new File(dest, "sling.properties");
+        if ( !origSlingProps.exists() ) {
+            throw new MojoExecutionException("sling.properties not found at " + origSlingProps);
+        }
+
+        // read original properties
+        final Properties orig = new Properties();
+        FileInputStream fis = null;
+        try {
+            fis = new FileInputStream(origSlingProps);
+            orig.load(fis);
+        } catch (final IOException ioe) {
+            throw new MojoExecutionException("Unable to read " + origSlingProps, ioe);
+        } finally {
+            if ( fis != null ) {
+                try { fis.close(); } catch (final IOException ignore) {}
+            }
+        }
+
+        // patch
+        final Enumeration<Object> keys = additionalProps.keys();
+        if ( keys.hasMoreElements() ) {
+            getLog().info("Patching sling.properties");
+        }
+        while ( keys.hasMoreElements() ) {
+            final Object key = keys.nextElement();
+            orig.put(key, additionalProps.get(key));
+        }
+
+        /// and save
+        FileOutputStream fos = null;
+        try {
+            fos = new FileOutputStream(origSlingProps);
+            orig.store(fos, null);
+        } catch (final IOException ioe) {
+            throw new MojoExecutionException("Unable to save " + origSlingProps, ioe);
+        } finally {
+            if ( fis != null ) {
+                try { fis.close(); } catch (final IOException ignore) {}
+            }
+        }
+    }
+
+    /**
+     * Patch the sling bootstrap command file
+     */
+    private void patchSlingBootstrap(final File dest, final String additionalCmd)
+    throws MojoExecutionException {
+        getLog().info("Patching sling_bootstrap.txt");
+        final File origSlingCmd = new File(dest, "sling_bootstrap.txt");
+        FileWriter writer = null;
+
+        /// and write
+        try {
+            writer = new FileWriter(origSlingCmd);
+
+            writer.write(additionalCmd);
+        } catch (final IOException ioe) {
+            throw new MojoExecutionException("Unable to save " + origSlingCmd, ioe);
+        } finally {
+            if ( writer != null ) {
+                try { writer.close(); } catch (final IOException ignore) {}
+            }
+        }
+    }
+
+    private void copyBaseArtifact() throws MojoExecutionException {
+        Artifact artifact = getBaseArtifact();
+        if (artifact == null) {
+            throw new MojoExecutionException(
+                    String.format("Project doesn't have a base dependency of groupId %s and artifactId %s",
+                                    base.getGroupId(), base.getArtifactId()));
+        }
+        File destinationDir = new File(getOutputDirectory(), baseDestination);
+        File destinationFile = new File(destinationDir, artifact
+                .getArtifactId()
+                + "." + artifact.getArtifactHandler().getExtension());
+
+        // check if custom sling.properties file or bootstrap command exists
+        final Properties additionalProps = this.getSlingProperties(JAR.equals(this.packaging));
+        final String bootstrapCmd = this.getSlingBootstrap(JAR.equals(this.packaging));
+        if ( additionalProps != null || bootstrapCmd != null ) {
+            // unpack to a temp destination
+            final File dest = new File(this.tempDirectory, "basejar");
+            try {
+                unpack(artifact.getFile(), dest);
+
+                // patch sling properties
+                if ( additionalProps != null ) {
+                    this.patchSlingProperties(dest, additionalProps);
+                }
+
+                // patch bootstrap command
+                if  ( bootstrapCmd != null ) {
+                    this.patchSlingBootstrap(dest, bootstrapCmd);
+                }
+
+                // and repack again
+                pack(dest, destinationFile);
+            } finally {
+                this.tempDirectory.delete();
+            }
+        } else {
+            // we can just copy
+            if (shouldCopy(artifact.getFile(), destinationFile)) {
+                try {
+                    getLog().info(
+                            String.format("Copying base artifact from %s to %s.",
+                                    artifact.getFile(), destinationFile));
+                    FileUtils.copyFile(artifact.getFile(), destinationFile);
+                } catch (IOException e) {
+                    throw new MojoExecutionException(
+                            "Unable to copy base artifact.", e);
+                }
+            } else {
+                getLog().debug(
+                        String.format("Skipping copy of base artifact from %s.",
+                                artifact.getFile()));
+            }
+        }
+    }
+
+    private Artifact getBaseArtifact() throws MojoExecutionException {
+        Artifact baseDependency = getBaseDependency();
+        if (baseDependency == null) {
+            return null;
+        }
+
+        return getArtifact(base.getGroupId(), base.getArtifactId(),
+                baseDependency.getVersion(), base.getType(), base
+                        .getClassifier());
+
+    }
+
+    private Artifact getBaseDependency() {
+        return project.getArtifactMap().get(
+                base.getGroupId() + ":" + base.getArtifactId());
+    }
+
+    protected File getOutputDirectory() {
+        if (WAR.equals(packaging)) {
+            return warOutputDirectory;
+        }
+        return buildOutputDirectory;
+    }
+
+    protected void unpackBaseArtifact() throws MojoExecutionException {
+        Artifact artifact = getBaseDependency();
+        if (artifact == null) {
+            throw new MojoExecutionException(
+                    String
+                            .format(
+                                    "Project doesn't have a base dependency of groupId %s and artifactId %s",
+                                    base.getGroupId(), base.getArtifactId()));
+        }
+        unpack(artifact.getFile(), buildOutputDirectory);
+    }
+
+    private void copyConfigurationFiles() throws MojoExecutionException {
+        final File configDir = this.getConfigDirectory();
+        if (configDir.exists() ) {
+            try {
+                copyDirectory(configDir, new File(getOutputDirectory(), CONFIG_PATH_PREFIX), null, FileUtils.getDefaultExcludes());
+            } catch (IOException e) {
+                throw new MojoExecutionException("Unable to copy configuration files", e);
+            }
+        }
+    }
+
+    private void unpack(File source, File destination)
+            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);
+
+            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);
+        }
+    }
+
+    private void pack(File sourceDir, File destination)
+    throws MojoExecutionException {
+        getLog().info("Packing " + sourceDir.getPath() + " to\n  " + destination.getPath());
+        try {
+            destination.getParentFile().mkdirs();
+
+            jarArchiver.setDestFile(destination);
+            jarArchiver.addDirectory(sourceDir);
+            jarArchiver.setManifest(new File(sourceDir, "META-INF/MANIFEST.MF".replace('/', File.separatorChar)));
+            jarArchiver.createArchive();
+        } catch (IOException e) {
+            throw new MojoExecutionException("Unable to pack " + sourceDir.getPath(), e);
+        } catch (ArchiverException e) {
+            throw new MojoExecutionException("Unable to pack " + sourceDir.getPath(), e);
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/PrepareTestWebAppMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/PrepareTestWebAppMojo.java
new file mode 100644
index 0000000..d38d1cf
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/PrepareTestWebAppMojo.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;
+
+import java.io.File;
+
+import org.apache.maven.artifact.handler.ArtifactHandler;
+import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+
+/**
+ * Initialize a Sling integration test webapp by extracting bundles into the
+ * correct locations, including the current artifact.
+ *
+ * @goal prepare-test-webapp
+ * @requiresDependencyResolution test
+ * @phase package
+ */
+public class PrepareTestWebAppMojo extends PreparePackageMojo {
+
+    /**
+     * The project's build directory (i.e. target).
+     *
+     * @parameter expression="${project.build.directory}"
+     * @readonly
+     */
+    private File buildDirectory;
+
+    /**
+     * The start level for the current artifact.
+     *
+     * @parameter default-value="16"
+     */
+    private int startLevel;
+
+    /**
+     * The output directory for bundles.
+     *
+     * @parameter default-value="${project.build.directory}/launchpad-bundles"
+     */
+    private File outputDirectory;
+
+    /**
+     * @component
+     */
+    private ArtifactHandlerManager artifactHandlerManager;
+
+    @Override
+    public void executeWithArtifacts() throws MojoExecutionException, MojoFailureException {
+        super.executeWithArtifacts();
+        copy(getPrimaryArtifact(), startLevel, null, getOutputDirectory());
+    }
+
+    @Override
+    protected File getOutputDirectory() {
+        return outputDirectory;
+    }
+
+
+    @Override
+    protected void unpackBaseArtifact() throws MojoExecutionException {
+        // No-op. This is JAR-specific.
+    }
+
+    private File getPrimaryArtifact() throws MojoExecutionException {
+        ArtifactHandler handler = artifactHandlerManager.getArtifactHandler(project.getPackaging());
+
+        String artifactName = project.getBuild().getFinalName() + "." + handler.getExtension();
+
+        File file = new File(buildDirectory, artifactName);
+        if (!file.exists()) {
+            throw new MojoExecutionException("Project's primary artifact (" + file.getPath() + ") doesn't exist.");
+        }
+        return file;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/RunMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/RunMojo.java
new file mode 100644
index 0000000..feaecd5
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/RunMojo.java
@@ -0,0 +1,101 @@
+/*
+ * 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.util.Map;
+
+import org.apache.felix.framework.Logger;
+import org.apache.sling.launchpad.api.LaunchpadContentProvider;
+import org.apache.sling.launchpad.base.impl.Sling;
+import org.osgi.framework.BundleException;
+
+/**
+ * Run a Launchpad application.
+ *
+ * @goal run
+ * @requiresDependencyResolution test
+ *
+ */
+public class RunMojo extends AbstractLaunchpadStartingMojo {
+
+
+
+    private Thread shutdown = new Thread() {
+        /**
+         * Called when the Java VM is being terminiated, for example because the
+         * KILL signal has been sent to the process. This method calls stop on
+         * the launched Sling instance to terminate the framework before
+         * returning.
+         */
+        @Override
+        public void run() {
+            getLog().info("Java VM is shutting down", null);
+            shutdown();
+        }
+
+        // ---------- Shutdown support for control listener and shutdown hook
+
+        void shutdown() {
+            // remove the shutdown hook, will fail if called from the
+            // shutdown hook itself. Otherwise this prevents shutdown
+            // from being called again
+            try {
+                Runtime.getRuntime().removeShutdownHook(this);
+            } catch (Throwable t) {
+                // don't care for problems removing the hook
+            }
+
+            stopSling();
+        }
+    };
+    
+    private boolean registeredHook = false;
+
+    protected Sling startSling(LaunchpadContentProvider resourceProvider, final Map<String, String> props, Logger logger)
+            throws BundleException {
+        if (!registeredHook) {
+            Runtime.getRuntime().addShutdownHook(shutdown);
+            registeredHook = true;
+        }
+        
+        // creating the instance launches the framework and we are done here
+        Sling mySling = new Sling(this, logger, resourceProvider, props) {
+
+            // overwrite the loadPropertiesOverride method to inject the
+            // mojo arguments unconditionally. These will not be persisted
+            // in any properties file, though
+            protected void loadPropertiesOverride(
+                    Map<String, String> properties) {
+                if (props != null) {
+                    properties.putAll(props);
+                }
+            }
+        };
+
+        // TODO this seems hacky!
+        try {
+            while (mySling != null) {
+                Thread.sleep(100);
+            }
+        } catch (InterruptedException e) {
+        }
+
+        return mySling;
+    }
+
+
+}
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/StartMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/StartMojo.java
new file mode 100644
index 0000000..4a48571
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/StartMojo.java
@@ -0,0 +1,67 @@
+/*
+ * 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.util.Map;
+
+import org.apache.felix.framework.Logger;
+import org.apache.sling.launchpad.api.LaunchpadContentProvider;
+import org.apache.sling.launchpad.base.impl.Sling;
+import org.osgi.framework.BundleException;
+
+/**
+ * Start a Launchpad application.
+ *
+ * @goal start
+ * @requiresDependencyResolution test
+ *
+ */
+public class StartMojo extends AbstractLaunchpadStartingMojo {
+
+    /**
+     * @parameter expression="${sling.control.port}" default-value="63000"
+     */
+    private int controlPort;
+
+    /**
+     * @parameter expression="${sling.control.host}"
+     */
+    private String controlHost;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected Sling startSling(LaunchpadContentProvider resourceProvider, final Map<String, String> props, Logger logger)
+            throws BundleException {
+        new ControlListener(this, getLog(), controlHost, controlPort).listen();
+
+        return new Sling(this, logger, resourceProvider, props) {
+
+            // overwrite the loadPropertiesOverride method to inject the
+            // mojo arguments unconditionally. These will not be persisted
+            // in any properties file, though
+            protected void loadPropertiesOverride(
+                    Map<String, String> properties) {
+                if (props != null) {
+                    properties.putAll(props);
+                }
+            }
+        };
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/StatusMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/StatusMojo.java
new file mode 100644
index 0000000..95388e5
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/StatusMojo.java
@@ -0,0 +1,49 @@
+/*
+ * 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 org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+
+/**
+ * Gets the status a running Launchpad standalone application.
+ *
+ * @goal status
+ * @requiresProject false
+ *
+ */
+public class StatusMojo extends AbstractMojo {
+
+    /**
+     * @parameter expression="${sling.control.port}" default-value="63000"
+     */
+    private int controlPort;
+
+    /**
+     * @parameter expression="${sling.control.host}" default-value="localhost"
+     */
+    private String controlHost;
+
+    /**
+     * {@inheritDoc}
+     */
+    public void execute() throws MojoExecutionException, MojoFailureException {
+        new ControlListener(null, getLog(), controlHost, controlPort).statusServer();
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/maven/projectsupport/StopMojo.java b/src/main/java/org/apache/sling/maven/projectsupport/StopMojo.java
new file mode 100644
index 0000000..8fe3a8f
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/StopMojo.java
@@ -0,0 +1,49 @@
+/*
+ * 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 org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+
+/**
+ * Stop a running Launchpad standalone application.
+ *
+ * @goal stop
+ * @requiresProject false
+ *
+ */
+public class StopMojo extends AbstractMojo {
+
+    /**
+     * @parameter expression="${sling.control.port}" default-value="63000"
+     */
+    private int controlPort;
+
+    /**
+     * @parameter expression="${sling.control.host}" default-value="localhost"
+     */
+    private String controlHost;
+
+    /**
+     * {@inheritDoc}
+     */
+    public void execute() throws MojoExecutionException, MojoFailureException {
+        new ControlListener(null, getLog(), controlHost, controlPort).shutdownServer();
+    }
+
+}
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..9ae1f8d
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/bundlelist/BaseBundle.java
@@ -0,0 +1,39 @@
+/*
+ * 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();
+
+    @Override
+    public String toString() {
+        return "Bundle [artifactId=" + getArtifactId() + ", classifier="
+                + getClassifier() + ", groupId=" + getGroupId() + ", startLevel="
+                + getStartLevel() + ", type=" + getType() + ", version=" + 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..1c7bc56
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/bundlelist/BaseBundleList.java
@@ -0,0 +1,115 @@
+/*
+ * 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.maven.shared.osgi.DefaultMaven2OsgiConverter;
+import org.apache.maven.shared.osgi.Maven2OsgiConverter;
+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.osgi.framework.Version;
+
+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(sl, 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) {
+       add(null, newBnd);
+    }
+
+    /**
+     * Merge bundle into a start level using the supplied level if present.
+     * @param mergeStartLevel
+     * @param newBnd
+     */
+    private void add(StartLevel mergeStartLevel, Bundle newBnd) {
+        Bundle current = get(newBnd, false);
+        if (current != null) {
+            final Maven2OsgiConverter converter = new DefaultMaven2OsgiConverter();
+
+            // compare versions, the highest will be used
+            final Version newVersion = new Version(converter.getVersion(newBnd.getVersion()));
+            final Version oldVersion = new Version(converter.getVersion(current.getVersion()));
+            if ( newVersion.compareTo(oldVersion) > 0 ) {
+                current.setVersion(newBnd.getVersion());
+            }
+        } else {
+            StartLevel startLevel = null;
+            if ( mergeStartLevel == null || newBnd.getStartLevel() != 0) {
+                startLevel = getOrCreateStartLevel(newBnd.getStartLevel());
+            } else {
+                startLevel = getOrCreateStartLevel(mergeStartLevel.getStartLevel());
+            }
+            startLevel.getBundles().add(newBnd);
+        }
+    }
+
+    private StartLevel getOrCreateStartLevel(int startLevel) {
+        for (StartLevel sl : getStartLevels()) {
+            if (sl.getStartLevel() == startLevel) {
+                return sl;
+            }
+        }
+
+        StartLevel sl = new StartLevel();
+        getStartLevels().add(sl);
+        sl.setRawLevel(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..8e541d0
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/projectsupport/bundlelist/BaseStartLevel.java
@@ -0,0 +1,91 @@
+/*
+ * 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 {
+
+    private static final String BOOT_MARKER = "boot";
+
+    public abstract List<Bundle> getBundles();
+
+    private int startLevel;
+
+    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());
+    }
+
+    /**
+     * Set the level field.
+     *
+     * @param level
+     */
+    public void setLevel( final String level ) {
+        if ( BOOT_MARKER.equalsIgnoreCase(level) ) {
+            this.startLevel = -1;
+        } else {
+            this.startLevel = Integer.valueOf(level);
+            if ( this.startLevel < 0 ) {
+                throw new IllegalArgumentException("Start level must either be '" + BOOT_MARKER + "' or non-negative: " + level);
+            }
+        }
+    }
+
+    public void setRawLevel( final int level ) {
+        this.startLevel = level;
+    }
+
+    public String getLevel() {
+        return (this.startLevel == -1 ? BOOT_MARKER : String.valueOf(this.startLevel));
+    }
+
+    public int getStartLevel() {
+        return this.startLevel;
+    }
+}
diff --git a/src/main/mdo/bundle-list.xml b/src/main/mdo/bundle-list.xml
new file mode 100644
index 0000000..0bb9d5c
--- /dev/null
+++ b/src/main/mdo/bundle-list.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0"?>
+<!--
+  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.
+-->
+<model xmlns="http://modello.codehaus.org/MODELLO/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://modello.codehaus.org/MODELLO/1.0.0 http://modello.codehaus.org/xsd/modello-1.0.0.xsd">
+    <id>bundle-list</id>
+    <name>BundleList</name>
+    <description>
+        <![CDATA[Model of a bundle list.]]>
+    </description>
+    <defaults>
+        <default>
+            <key>package</key>
+            <value>org.apache.sling.maven.projectsupport.bundlelist</value>
+        </default>
+    </defaults>
+    <classes>
+        <class rootElement="true" xml.tagName="bundles">
+            <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>
+                    <version>1.0.0</version>
+                    <association xml.itemsStyle="flat">
+                        <type>StartLevel</type>
+                        <multiplicity>*</multiplicity>
+                    </association>
+                </field>
+            </fields>
+        </class>
+        <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" java.setter="false" java.getter="false">
+            		<name>level</name>
+            		<version>1.0.0</version>
+            		<type>String</type>
+            	</field>
+                <field>
+                    <name>bundles</name>
+                    <version>1.0.0</version>
+                    <association xml.itemsStyle="flat">
+                        <type>Bundle</type>
+                        <multiplicity>*</multiplicity>
+                    </association>
+                </field>
+            </fields>
+        </class>
+        <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>
+                    <version>1.0.0</version>
+                    <type>String</type>
+                    <required>true</required>
+                </field>
+                <field>
+                    <name>artifactId</name>
+                    <version>1.0.0</version>
+                    <type>String</type>
+                    <required>true</required>
+                </field>
+                <field>
+                    <name>version</name>
+                    <version>1.0.0</version>
+                    <type>String</type>
+                    <required>true</required>
+                </field>
+                <field>
+                    <name>type</name>
+                    <version>1.0.0</version>
+                    <type>String</type>
+                    <defaultValue>jar</defaultValue>
+                </field>
+                <field>
+                    <name>classifier</name>
+                    <version>1.0.0</version>
+                    <type>String</type>
+                </field>
+                <field>
+                    <name>startLevel</name>
+                    <version>1.0.0</version>
+                    <type>int</type>
+                    <required>true</required>
+                </field>
+                <field>
+                    <name>runModes</name>
+                    <version>1.0.0</version>
+                    <type>String</type>
+                </field>
+            </fields>
+        </class>
+    </classes>
+</model>
diff --git a/src/main/resources/META-INF/plexus/components.xml b/src/main/resources/META-INF/plexus/components.xml
new file mode 100644
index 0000000..d6fd19c
--- /dev/null
+++ b/src/main/resources/META-INF/plexus/components.xml
@@ -0,0 +1,53 @@
+<!--
+ 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.
+-->
+<component-set>
+  <components>
+    <component>
+      <role>org.apache.maven.lifecycle.mapping.LifecycleMapping</role>
+      <role-hint>partialbundlelist</role-hint>
+      <implementation>org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping</implementation>
+      <configuration>
+        <lifecycles>
+          <lifecycle>
+            <id>default</id>
+            <!-- START SNIPPET: bundle-lifecycle -->
+            <phases>
+              <package>org.apache.sling:maven-launchpad-plugin:attach-partial-bundle-list</package>
+              <install>org.apache.maven.plugins:maven-install-plugin:install</install>
+              <deploy>org.apache.maven.plugins:maven-deploy-plugin:deploy</deploy>
+            </phases>
+            <!-- END SNIPPET: bundle-lifecycle -->
+          </lifecycle>
+        </lifecycles>
+      </configuration>
+    </component>
+    <component>
+      <role>org.apache.maven.artifact.handler.ArtifactHandler</role>
+      <role-hint>partialbundlelist</role-hint>
+      <implementation>org.apache.maven.artifact.handler.DefaultArtifactHandler</implementation>
+      <configuration>
+        <type>partialbundlelist</type>
+        <includesDependencies>false</includesDependencies>
+        <language>xml</language>
+        <extension>xml</extension>
+        <addedToClasspath>false</addedToClasspath>
+      </configuration>
+    </component>
+  </components>
+</component-set>
diff --git a/src/main/resources/org/apache/sling/maven/projectsupport/dependencies.properties b/src/main/resources/org/apache/sling/maven/projectsupport/dependencies.properties
new file mode 100644
index 0000000..d8102e3
--- /dev/null
+++ b/src/main/resources/org/apache/sling/maven/projectsupport/dependencies.properties
@@ -0,0 +1,20 @@
+#  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.
+base=org.apache.sling,org.apache.sling.launchpad.base,,jar,,0
+defaultBundles=org.apache.sling,org.apache.sling.launchpad,RELEASE,jar,bundles,0
+defaultBundleList=org.apache.sling,org.apache.sling.launchpad,RELEASE,xml,bundlelist,0
+jarWebSupport=org.apache.felix,org.apache.felix.http.jetty,RELEASE,jar,,-1
diff --git a/src/main/resources/org/apache/sling/maven/projectsupport/drools-globals.drl b/src/main/resources/org/apache/sling/maven/projectsupport/drools-globals.drl
new file mode 100644
index 0000000..58a5d6c
--- /dev/null
+++ b/src/main/resources/org/apache/sling/maven/projectsupport/drools-globals.drl
@@ -0,0 +1,24 @@
+# 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.
+
+# this file ensures that the globals are always defined, even if they're not needed
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.project.MavenProject;
+
+global MavenSession mavenSession
+global MavenProject mavenProject
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/maven/projectsupport/PreparePackageMojoTest.java b/src/test/java/org/apache/sling/maven/projectsupport/PreparePackageMojoTest.java
new file mode 100644
index 0000000..2a2fff3
--- /dev/null
+++ b/src/test/java/org/apache/sling/maven/projectsupport/PreparePackageMojoTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import org.junit.Test;
+
+/**
+ * Tests of PreparePageMojo
+ */
+public class PreparePackageMojoTest {
+
+	@Test
+	public void testInitArtifactDefinitionsAllDefaults() throws Exception {
+		PreparePackageMojo mojo = new PreparePackageMojo();
+		invokeInitArtifactDefinitions(mojo);
+
+		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, "defaultBundleList", "org.apache.sling",
+		        "org.apache.sling.launchpad", "RELEASE", "xml", "bundlelist", 0);
+
+		makeArtifactAssertions(mojo, "jarWebSupport", "org.apache.felix",
+				"org.apache.felix.http.jetty", "RELEASE", "jar", null, -1);
+
+	}
+
+	private void makeArtifactAssertions(PreparePackageMojo mojo, String name,
+			String groupId, String artifactId, String version, String type,
+			String classifier, int startLevel) throws Exception {
+		ArtifactDefinition def = getArtifactDefinition(mojo, name);
+
+		assertNotNull(def);
+		assertEquals(groupId, def.getGroupId());
+		assertEquals(artifactId, def.getArtifactId());
+		assertEquals(version, def.getVersion());
+		assertEquals(type, def.getType());
+		assertEquals(classifier, def.getClassifier());
+		assertEquals(startLevel, def.getStartLevel());
+	}
+
+	private void invokeInitArtifactDefinitions(PreparePackageMojo mojo)
+			throws Exception {
+		Method method = findMethod(mojo.getClass(), "initArtifactDefinitions");
+		method.setAccessible(true);
+		method.invoke(mojo);
+		method.setAccessible(false);
+	}
+
+	private Method findMethod(Class<?> clazz, String name, Class<?>... args)
+			throws NoSuchMethodException {
+		while (clazz != Object.class) {
+			try {
+				return clazz.getDeclaredMethod(name, args);
+			} catch (SecurityException e) {
+			} catch (NoSuchMethodException e) {
+			}
+			clazz = clazz.getSuperclass();
+		}
+
+		throw new NoSuchMethodException("Could not find method " + name);
+	}
+
+	private Field findField(Class<?> clazz, String name)
+			throws NoSuchFieldException {
+		while (clazz != Object.class) {
+			try {
+				return clazz.getDeclaredField(name);
+			} catch (SecurityException e) {
+			} catch (NoSuchFieldException e) {
+			}
+			clazz = clazz.getSuperclass();
+		}
+
+		throw new NoSuchFieldException("Could not find field " + name);
+	}
+
+	private ArtifactDefinition getArtifactDefinition(PreparePackageMojo mojo,
+			String name) throws Exception {
+		Field field = findField(mojo.getClass(), name);
+		field.setAccessible(true);
+		ArtifactDefinition def = (ArtifactDefinition) field.get(mojo);
+		field.setAccessible(false);
+		return def;
+	}
+
+}