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;
+ }
+
+}