Author: rombert
Date: Thu Nov 5 17:05:46 2015
New Revision: 1712818
URL: http://svn.apache.org/viewvc?rev=1712818&view=rev
Log:
SLING-5149 - Generate OSGi subsystem intermediary file in
slingstart-maven-plugin
Submitted-By: David Bosschaert
Added:
sling/trunk/tooling/maven/slingstart-maven-plugin/src/main/resources/subsystem-base/
sling/trunk/tooling/maven/slingstart-maven-plugin/src/main/resources/subsystem-base/readme.txt
sling/trunk/tooling/maven/slingstart-maven-plugin/src/test/
sling/trunk/tooling/maven/slingstart-maven-plugin/src/test/java/
sling/trunk/tooling/maven/slingstart-maven-plugin/src/test/java/org/
sling/trunk/tooling/maven/slingstart-maven-plugin/src/test/java/org/apache/
sling/trunk/tooling/maven/slingstart-maven-plugin/src/test/java/org/apache/sling/
sling/trunk/tooling/maven/slingstart-maven-plugin/src/test/java/org/apache/sling/maven/
sling/trunk/tooling/maven/slingstart-maven-plugin/src/test/java/org/apache/sling/maven/slingstart/
sling/trunk/tooling/maven/slingstart-maven-plugin/src/test/java/org/apache/sling/maven/slingstart/PreparePackageMojoTest.java
Modified:
sling/trunk/tooling/maven/slingstart-maven-plugin/pom.xml
sling/trunk/tooling/maven/slingstart-maven-plugin/src/main/java/org/apache/sling/maven/slingstart/PreparePackageMojo.java
Modified: sling/trunk/tooling/maven/slingstart-maven-plugin/pom.xml
URL:
http://svn.apache.org/viewvc/sling/trunk/tooling/maven/slingstart-maven-plugin/pom.xml?rev=1712818&r1=1712817&r2=1712818&view=diff
==============================================================================
--- sling/trunk/tooling/maven/slingstart-maven-plugin/pom.xml (original)
+++ sling/trunk/tooling/maven/slingstart-maven-plugin/pom.xml Thu Nov 5
17:05:46 2015
@@ -86,7 +86,7 @@
<dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.provisioning.model</artifactId>
- <version>1.3.0</version>
+ <version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
@@ -168,5 +168,55 @@
</exclusion>
</exclusions>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.10.19</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <!-- The following artifacts are purely used by the unit tests
+ so these dependencies ensures that they are in the .m2 directory
-->
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.commons.classloader</artifactId>
+ <version>1.3.2</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.commons.contentdetection</artifactId>
+ <version>1.0.2</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.commons.json</artifactId>
+ <version>2.0.12</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.commons.mime</artifactId>
+ <version>2.1.8</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.commons.osgi</artifactId>
+ <version>2.3.0</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.commons.threads</artifactId>
+ <version>3.2.0</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
Modified:
sling/trunk/tooling/maven/slingstart-maven-plugin/src/main/java/org/apache/sling/maven/slingstart/PreparePackageMojo.java
URL:
http://svn.apache.org/viewvc/sling/trunk/tooling/maven/slingstart-maven-plugin/src/main/java/org/apache/sling/maven/slingstart/PreparePackageMojo.java?rev=1712818&r1=1712817&r2=1712818&view=diff
==============================================================================
---
sling/trunk/tooling/maven/slingstart-maven-plugin/src/main/java/org/apache/sling/maven/slingstart/PreparePackageMojo.java
(original)
+++
sling/trunk/tooling/maven/slingstart-maven-plugin/src/main/java/org/apache/sling/maven/slingstart/PreparePackageMojo.java
Thu Nov 5 17:05:46 2015
@@ -16,15 +16,25 @@
*/
package org.apache.sling.maven.slingstart;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.jar.Attributes;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
import org.apache.commons.io.IOUtils;
import org.apache.felix.cm.file.ConfigurationHandler;
@@ -41,9 +51,11 @@ import org.apache.maven.plugins.annotati
import org.apache.sling.provisioning.model.ArtifactGroup;
import org.apache.sling.provisioning.model.Configuration;
import org.apache.sling.provisioning.model.Feature;
+import org.apache.sling.provisioning.model.FeatureTypes;
import org.apache.sling.provisioning.model.Model;
import org.apache.sling.provisioning.model.ModelConstants;
import org.apache.sling.provisioning.model.RunMode;
+import org.apache.sling.provisioning.model.Section;
import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.archiver.UnArchiver;
import org.codehaus.plexus.archiver.manager.ArchiverManager;
@@ -60,6 +72,7 @@ import org.codehaus.plexus.util.FileUtil
threadSafe = true
)
public class PreparePackageMojo extends AbstractSlingStartMojo {
+ private static final String ALL_RUNMODES_KEY = "_all_";
private static final String BASE_DESTINATION = "resources";
@@ -93,6 +106,10 @@ public class PreparePackageMojo extends
public void execute() throws MojoExecutionException, MojoFailureException {
final Model model = ProjectHelper.getEffectiveModel(this.project,
getResolverOptions());
+ execute(model);
+ }
+
+ void execute(final Model model) throws MojoExecutionException {
this.prepareGlobal(model);
this.prepareStandaloneApp(model);
this.prepareWebapp(model);
@@ -177,22 +194,34 @@ public class PreparePackageMojo extends
for(final Feature feature : model.getFeatures()) {
if ( feature.isSpecial() &&
!feature.getName().equals(ModelConstants.FEATURE_BOOT)) {
continue;
- }
- for(final RunMode runMode : feature.getRunModes()) {
- if ( packageRunMode == null ) {
- if ( runMode.isSpecial() ) {
- continue;
- }
- this.buildContentsMap(model, runMode, contentsMap,
feature.getName().equals(ModelConstants.FEATURE_BOOT));
- } else {
- if ( runMode.isRunMode(packageRunMode) ) {
+ } else if
(FeatureTypes.SUBSYSTEM_APPLICATION.equals(feature.getType()) ||
+ FeatureTypes.SUBSYSTEM_COMPOSITE.equals(feature.getType())
||
+ FeatureTypes.SUBSYSTEM_FEATURE.equals(feature.getType())) {
+ buildSubsystemBase(contentsMap, feature);
+ } else {
+ for(final RunMode runMode : feature.getRunModes()) {
+ if ( packageRunMode == null ) {
+ if ( runMode.isSpecial() ) {
+ continue;
+ }
this.buildContentsMap(model, runMode, contentsMap,
feature.getName().equals(ModelConstants.FEATURE_BOOT));
+ } else {
+ if ( runMode.isRunMode(packageRunMode) ) {
+ this.buildContentsMap(model, runMode, contentsMap,
feature.getName().equals(ModelConstants.FEATURE_BOOT));
+ }
}
}
}
}
}
+ private void buildSubsystemBase(final Map<String, File> contentsMap, final
Feature feature) throws MojoExecutionException {
+ AtomicInteger startLevelHolder = new AtomicInteger(); // Used as
output argument
+ File subsystemFile = createSubsystemBaseFile(feature,
startLevelHolder);
+ if (subsystemFile != null)
+ contentsMap.put(getPathForArtifact(startLevelHolder.get(),
subsystemFile.getName()), subsystemFile);
+ }
+
/**
* Build a list of all artifacts from this run mode
*/
@@ -235,6 +264,164 @@ public class PreparePackageMojo extends
}
}
+
+ private File createSubsystemBaseFile(Feature feature, AtomicInteger
startLevelHolder) throws MojoExecutionException {
+ File subsystemFile = new File(getTmpDir(), feature.getName() +
".subsystem-base");
+ if (subsystemFile.exists()) {
+ // This subsystem has already been created
+ // TODO is there a better way to avoid calling this multiple times?
+ return null;
+ }
+
+ startLevelHolder.set(-1);
+
+ // The runmodes information has to be the first item in the archive so
that we always have it available when the
+ // archive is being processed. For this reason a Jar file is used here
as it's guaranteed to store the manifest
+ // first.
+ Manifest runModesManifest = getRunModesManifest(feature);
+
+ getLog().info("Creating subsystem base file: " +
subsystemFile.getName());
+ subsystemFile.getParentFile().mkdirs();
+
+ try (JarOutputStream os = new JarOutputStream(new
FileOutputStream(subsystemFile), runModesManifest)) {
+ Map<String, Integer> bsnStartOrderMap = new HashMap<>();
+
+ for (RunMode rm : feature.getRunModes()) {
+ for (ArtifactGroup ag : rm.getArtifactGroups()) {
+ int startOrder = ag.getStartLevel(); // For subsystems the
start level on the artifact group is used as start order.
+
+ for (org.apache.sling.provisioning.model.Artifact a : ag) {
+ Artifact artifact =
ModelUtils.getArtifact(this.project, this.mavenSession,
this.artifactHandlerManager, this.resolver,
+ a.getGroupId(), a.getArtifactId(),
a.getVersion(), a.getType(), a.getClassifier());
+ File artifactFile = artifact.getFile();
+ String entryName = getEntryName(artifactFile,
startOrder);
+
+ ZipEntry ze = new ZipEntry(entryName);
+ try {
+ os.putNextEntry(ze);
+ Files.copy(artifactFile.toPath(), os);
+ } finally {
+ os.closeEntry();
+ }
+ }
+ }
+ }
+
+ int sl = createSubsystemManifest(feature, bsnStartOrderMap, os);
+ if (sl != -1)
+ startLevelHolder.set(sl);
+ addReadme(os);
+ } catch (IOException ioe) {
+ throw new MojoExecutionException("Problem creating subsystem .esa
file " + subsystemFile, ioe);
+ }
+ return subsystemFile;
+ }
+
+ private Manifest getRunModesManifest(Feature feature) throws
MojoExecutionException {
+ Map<String, StringBuilder> runModes = new HashMap<>();
+
+ for (RunMode rm : feature.getRunModes()) {
+ for (ArtifactGroup ag : rm.getArtifactGroups()) {
+ int startOrder = ag.getStartLevel(); // For subsystems the
start level on the artifact group is used as start order.
+
+ for (org.apache.sling.provisioning.model.Artifact a : ag) {
+ Artifact artifact = ModelUtils.getArtifact(this.project,
this.mavenSession, this.artifactHandlerManager, this.resolver,
+ a.getGroupId(), a.getArtifactId(), a.getVersion(),
a.getType(), a.getClassifier());
+ File artifactFile = artifact.getFile();
+ String entryName = getEntryName(artifactFile, startOrder);
+
+ String [] runModeNames = rm.getNames();
+ if (runModeNames == null)
+ runModeNames = new String[] {ALL_RUNMODES_KEY};
+
+ for (String runModeName : runModeNames) {
+ StringBuilder sb = runModes.get(runModeName);
+ if (sb == null) {
+ sb = new StringBuilder();
+ runModes.put(runModeName, sb);
+ } else {
+ sb.append('|');
+ }
+
+ sb.append(entryName);
+ }
+ }
+ }
+ }
+
+ Manifest mf = new Manifest();
+ Attributes attrs = mf.getMainAttributes();
+ attrs.putValue("Manifest-Version", "1.0"); // Manifest does not work
without this value
+ attrs.putValue("About-This-Manifest", "This is not a real manifest, it
is used as information when this archive is transformed into a real subsystem
.esa file");
+ for (Map.Entry<String, StringBuilder> entry : runModes.entrySet()) {
+ attrs.putValue(entry.getKey(), entry.getValue().toString());
+ }
+ return mf;
+ }
+
+ private String getEntryName(File artifactFile, int startOrder) {
+ return "Potential_Bundles/" + startOrder + "/" +
artifactFile.getName();
+ }
+
+ // This manifest will be used as the basis for the OSGI-INF/SUBSYSTEM.MF
file when the real
+ // .esa file is generated. However since some contents of that file depend
on the actual
+ // runmode that is being executed, additional information will be added to
the SUBSYSTEM.MF
+ // file at startup time before it's finalized (example: Subsystem-Content).
+ private int createSubsystemManifest(Feature feature,
+ Map<String, Integer> startOrderMap, ZipOutputStream os) throws
IOException {
+ int subsystemStartLevel = -1;
+ ZipEntry ze = new ZipEntry("SUBSYSTEM-MANIFEST-BASE.MF");
+ try {
+ os.putNextEntry(ze);
+
+ Manifest mf = new Manifest();
+ Attributes attributes = mf.getMainAttributes();
+ attributes.putValue("Manifest-Version", "1.0"); // Manifest does
not work without this value
+ attributes.putValue("Subsystem-SymbolicName", feature.getName());
+ attributes.putValue("Subsystem-Version", "1"); // Version must be
an integer (cannot be a long), TODO better idea?
+ attributes.putValue("Subsystem-Type", feature.getType());
+ for (Section section :
feature.getAdditionalSections("subsystem-manifest")) {
+ String sl = section.getAttributes().get("startLevel");
+ try {
+ subsystemStartLevel = Integer.parseInt(sl);
+ } catch (NumberFormatException nfe) {
+ // Not a valid start level
+ }
+
+ BufferedReader br = new BufferedReader(new
StringReader(section.getContents()));
+ String line = null;
+ while ((line = br.readLine()) != null) {
+ int idx = line.indexOf(':');
+ if (idx > 0) {
+ String key = line.substring(0, idx);
+ String value;
+ idx++;
+ if (line.length() > idx)
+ value = line.substring(idx);
+ else
+ value = "";
+ attributes.putValue(key.trim(), value.trim());
+ }
+ }
+ }
+ mf.write(os);
+ } finally {
+ os.closeEntry();
+ }
+
+ return subsystemStartLevel;
+ }
+
+ private void addReadme(ZipOutputStream os) throws IOException {
+ ZipEntry ze = new ZipEntry("readme.txt");
+ try (InputStream is =
getClass().getResourceAsStream("/subsystem-base/readme.txt")) {
+ os.putNextEntry(ze);
+ IOUtils.copy(is, os);
+ } finally {
+ os.closeEntry();
+ }
+ }
+
/**
* Build the settings for the given packaging run mode
*/
@@ -385,12 +572,16 @@ public class PreparePackageMojo extends
}
}
+ private String getPathForArtifact(final int startLevel, final String
artifactName) {
+ return getPathForArtifact(startLevel, artifactName, null, false);
+ }
+
/**
* Get the relative path for an artifact.
*/
private String getPathForArtifact(final int startLevel, final String
artifactName, final RunMode rm, final boolean isBoot) {
final Set<String> runModesList = new TreeSet<String>();
- if (rm.getNames() != null ) {
+ if ( rm != null && rm.getNames() != null ) {
for(final String mode : rm.getNames()) {
runModesList.add(mode);
}
Added:
sling/trunk/tooling/maven/slingstart-maven-plugin/src/main/resources/subsystem-base/readme.txt
URL:
http://svn.apache.org/viewvc/sling/trunk/tooling/maven/slingstart-maven-plugin/src/main/resources/subsystem-base/readme.txt?rev=1712818&view=auto
==============================================================================
---
sling/trunk/tooling/maven/slingstart-maven-plugin/src/main/resources/subsystem-base/readme.txt
(added)
+++
sling/trunk/tooling/maven/slingstart-maven-plugin/src/main/resources/subsystem-base/readme.txt
Thu Nov 5 17:05:46 2015
@@ -0,0 +1,36 @@
+A .subsystem-base jar file is produced from the sling provisioning model by
the sling-maven-plugin as an intermediary file that is turned into a subsystem
file .esa at runtime by the org.apache.sling.installer.factory.subsystems-base
bundle. The actual content of the subsystem at runtime is determined by the
current run mode. The transformer creates an .esa file that contains the right
resources according to the current runmode.
+
+A subsystem-base file is generated from features in the sling provisioning
model of type osgi.subsystem.application, osgi.subsystem.composite or
osgi.subsystem.feature. For example:
+
+[feature name=mysubsystem type=osgi.subsystem.feature]
+
+[:subsystem-manifest startLevel=20]
+ Subsystem-Description: Extra subsystem headers can go here
+ Subsystem-Copyright: (c) 2015 for example
+
+[artifacts]
+ com.foo.bar/bundle/1
+
+[artifacts startLevel=10]
+ com.foo.bar/bundle1/1.2.3
+ com.foo.bar/bundle2/1.0.0
+
+[artifacts startLevel=15 runModes=myrunmode]
+ com.foo.bar/bundle3/0.0.1
+
+The above provisioning model description will generate a
mysubsystem.subsystem-base jar file. The bundles in the subsystem will *all*
have start level 20, from the startLevel in the :subsystem-manifest section.
The mysubsystem.subsystem-base file will be placed in the
install/20/mysubsystem.subsystem-base location of the generated
sling-maven-plugin generated slingstart artifact. The startLevel attribute on
the artifacts section are transformed into start-order attributes on the
Subsystem-Content header.
+The generated mysubsystem.subsystem-base file will have the following content:
+
+META-INF/MANIFEST.MF
+Potential_Bundles/0/bundle-1.jar
+Potential_Bundles/10/bundle1-1.2.3.jar
+Potential_Bundles/10/bundle2-1.0.0.jar
+Potential_Bundles/15/bundle3-0.0.1.jar
+SUBSYSTEM-MANIFEST-BASE.MF
+readme.txt (this file).
+
+The META-INF/MANIFEST.MF file contains information about which resources
should be deployed based on runmode. There is a special identifier _all_ which
lists resources that should be deployed in all runmodes.
+The Potential_Bundles locations hold the actual resources in directories that
denote their start-order.
+The SUBSYSTEM-MANIFEST-BASE.MF file is used as the basis for the
OSGI-INF/SUBSYSTEM.MF file in the .esa file. The Subsystem-SymbolicName,
Subsystem-Type, Subsystem-Version and Subsystem-Content are automatically
generated. Additional headers listed under the [:subsystem-manifest] section
are added to the ultimate SUBSYSTEM.MF. The Subsystem-Content is generated by
the SubsystemBaseTransformer based on the current runmode.
+
+This file is included in the .subsystem-base file as a readme as this is not a
common file format.
Added:
sling/trunk/tooling/maven/slingstart-maven-plugin/src/test/java/org/apache/sling/maven/slingstart/PreparePackageMojoTest.java
URL:
http://svn.apache.org/viewvc/sling/trunk/tooling/maven/slingstart-maven-plugin/src/test/java/org/apache/sling/maven/slingstart/PreparePackageMojoTest.java?rev=1712818&view=auto
==============================================================================
---
sling/trunk/tooling/maven/slingstart-maven-plugin/src/test/java/org/apache/sling/maven/slingstart/PreparePackageMojoTest.java
(added)
+++
sling/trunk/tooling/maven/slingstart-maven-plugin/src/test/java/org/apache/sling/maven/slingstart/PreparePackageMojoTest.java
Thu Nov 5 17:05:46 2015
@@ -0,0 +1,239 @@
+/*
+ * 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.slingstart;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.lang.reflect.Field;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.maven.artifact.DefaultArtifact;
+import org.apache.maven.artifact.handler.ArtifactHandler;
+import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
+import org.apache.maven.artifact.resolver.ArtifactResolver;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.model.Build;
+import org.apache.maven.project.MavenProject;
+import org.apache.sling.provisioning.model.Model;
+import org.apache.sling.provisioning.model.io.ModelReader;
+import org.codehaus.plexus.archiver.UnArchiver;
+import org.codehaus.plexus.archiver.manager.ArchiverManager;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+public class PreparePackageMojoTest {
+ @Test
+ public void testSubsystemBaseGeneration1() throws Exception {
+ // Provide the system with some artifacts that are known to be in the
local .m2 repo
+ // These are explicitly included in the test section of the pom.xml
+ PreparePackageMojo ppm = getMojoUnderTest(
+ "org.apache.sling/org.apache.sling.commons.classloader/1.3.2",
+
"org.apache.sling/org.apache.sling.commons.classloader/1.3.2/app",
+
"org.apache.sling/org.apache.sling.commons.contentdetection/1.0.2",
+ "org.apache.sling/org.apache.sling.commons.json/2.0.12",
+ "org.apache.sling/org.apache.sling.commons.mime/2.1.8",
+ "org.apache.sling/org.apache.sling.commons.osgi/2.3.0",
+ "org.apache.sling/org.apache.sling.commons.threads/3.2.0");
+
+ try {
+ // The launchpad feature is a prerequisite for the model
+ String modelTxt =
+ "[feature name=:launchpad]\n" +
+ "[artifacts]\n" +
+ "
org.apache.sling/org.apache.sling.commons.classloader/1.3.2\n" +
+ "" +
+ "[feature name=test1 type=osgi.subsystem.composite]\n" +
+ "" +
+ "[:subsystem-manifest startLevel=123]\n" +
+ " Subsystem-Description: Extra subsystem headers can go
here including very long ones that would span multiple lines in a manifest\n" +
+ " Subsystem-Copyright: (c) 2015 yeah!\n" +
+ "" +
+ "[artifacts]\n" +
+ " org.apache.sling/org.apache.sling.commons.osgi/2.3.0\n"
+
+ "" +
+ "[artifacts startLevel=10]\n" +
+ "
org.apache.sling/org.apache.sling.commons.json/2.0.12\n" +
+ " org.apache.sling/org.apache.sling.commons.mime/2.1.8\n"
+
+ "" +
+ "[artifacts startLevel=20 runModes=foo,bar]\n" +
+ "
org.apache.sling/org.apache.sling.commons.threads/3.2.0\n" +
+ "" +
+ "[artifacts startLevel=100 runModes=bar]\n" +
+ "
org.apache.sling/org.apache.sling.commons.contentdetection/1.0.2\n";
+ Model model = ModelReader.read(new StringReader(modelTxt), null);
+ ppm.execute(model);
+
+ File generatedFile = new File(ppm.getTmpDir() +
"/test1.subsystem-base");
+ try (JarFile jf = new JarFile(generatedFile)) {
+ // Test META-INF/MANIFEST.MF
+ Manifest mf = jf.getManifest();
+ Attributes attrs = mf.getMainAttributes();
+ String expected =
"Potential_Bundles/0/org.apache.sling.commons.osgi-2.3.0.jar|"
+ +
"Potential_Bundles/10/org.apache.sling.commons.json-2.0.12.jar|"
+ +
"Potential_Bundles/10/org.apache.sling.commons.mime-2.1.8.jar";
+ assertEquals(expected, attrs.getValue("_all_"));
+
assertEquals("Potential_Bundles/20/org.apache.sling.commons.threads-3.2.0.jar",
attrs.getValue("foo"));
+
assertEquals("Potential_Bundles/20/org.apache.sling.commons.threads-3.2.0.jar|"
+ +
"Potential_Bundles/100/org.apache.sling.commons.contentdetection-1.0.2.jar",
attrs.getValue("bar"));
+
+ // Test SUBSYSTEM-MANIFEST-BASE.MF
+ ZipEntry smbZE = jf.getEntry("SUBSYSTEM-MANIFEST-BASE.MF");
+ try (InputStream smbIS = jf.getInputStream(smbZE)) {
+ Manifest smbMF = new Manifest(smbIS);
+ Attributes smbAttrs = smbMF.getMainAttributes();
+ assertEquals("test1",
smbAttrs.getValue("Subsystem-SymbolicName"));
+ assertEquals("osgi.subsystem.composite",
smbAttrs.getValue("Subsystem-Type"));
+ assertEquals("(c) 2015 yeah!",
smbAttrs.getValue("Subsystem-Copyright"));
+ assertEquals("Extra subsystem headers can go here
including very long ones "
+ + "that would span multiple lines in a manifest",
+ smbAttrs.getValue("Subsystem-Description"));
+ }
+
+ // Test embedded bundles
+ File mrr = getMavenRepoRoot();
+ File soj = getMavenArtifactFile(mrr, "org.apache.sling",
"org.apache.sling.commons.osgi", "2.3.0");
+ ZipEntry sojZE =
jf.getEntry("Potential_Bundles/0/org.apache.sling.commons.osgi-2.3.0.jar");
+ try (InputStream is = jf.getInputStream(sojZE)) {
+ assertArtifactsEqual(soj, is);
+ }
+
+ File sjj = getMavenArtifactFile(mrr, "org.apache.sling",
"org.apache.sling.commons.json", "2.0.12");
+ ZipEntry sjZE =
jf.getEntry("Potential_Bundles/10/org.apache.sling.commons.json-2.0.12.jar");
+ try (InputStream is = jf.getInputStream(sjZE)) {
+ assertArtifactsEqual(sjj, is);
+ }
+
+ File smj = getMavenArtifactFile(mrr, "org.apache.sling",
"org.apache.sling.commons.mime", "2.1.8");
+ ZipEntry smjZE =
jf.getEntry("Potential_Bundles/10/org.apache.sling.commons.mime-2.1.8.jar");
+ try (InputStream is = jf.getInputStream(smjZE)) {
+ assertArtifactsEqual(smj, is);
+ }
+
+ File stj = getMavenArtifactFile(mrr, "org.apache.sling",
"org.apache.sling.commons.threads", "3.2.0");
+ ZipEntry stjZE =
jf.getEntry("Potential_Bundles/20/org.apache.sling.commons.threads-3.2.0.jar");
+ try (InputStream is = jf.getInputStream(stjZE)) {
+ assertArtifactsEqual(stj, is);
+ }
+
+ File ctj = getMavenArtifactFile(mrr, "org.apache.sling",
"org.apache.sling.commons.contentdetection", "1.0.2");
+ ZipEntry ctjZE =
jf.getEntry("Potential_Bundles/100/org.apache.sling.commons.contentdetection-1.0.2.jar");
+ try (InputStream is = jf.getInputStream(ctjZE)) {
+ assertArtifactsEqual(ctj, is);
+ }
+ }
+ } finally {
+ FileUtils.deleteDirectory(new
File(ppm.project.getBuild().getDirectory()));
+ }
+ }
+
+ private void assertArtifactsEqual(File f, InputStream is) throws
IOException {
+ byte[] bytes1 = Files.readAllBytes(f.toPath());
+ byte[] bytes2 = IOUtils.toByteArray(is);
+ assertArrayEquals("Bytes not equal on file " + f.getName(), bytes1,
bytes2);
+ }
+
+ private PreparePackageMojo getMojoUnderTest(String ... knownArtifacts)
throws Exception {
+ File mrr = getMavenRepoRoot();
+
+ ArtifactHandler ah = Mockito.mock(ArtifactHandler.class);
+ ArtifactHandlerManager ahm =
Mockito.mock(ArtifactHandlerManager.class);
+
Mockito.when(ahm.getArtifactHandler(Mockito.anyString())).thenReturn(ah);
+
+ Set<org.apache.maven.artifact.Artifact> artifacts = new HashSet<>();
+ for (String s : knownArtifacts) {
+ String[] parts = s.split("[/]");
+ switch (parts.length) {
+ case 3:
+ artifacts.add(getMavenArtifact(mrr, ah, parts[0], parts[1],
parts[2]));
+ break;
+ case 4:
+ artifacts.add(getMavenArtifact(mrr, ah, parts[0], parts[1],
parts[2], parts[3]));
+ break;
+ default: throw new IllegalStateException(s);
+ }
+ }
+
+ MavenProject mavenPrj = new MavenProject();
+ Build build = new Build();
+ Path tempDir = Files.createTempDirectory(getClass().getSimpleName());
+ build.setOutputDirectory(tempDir.toString());
+ build.setDirectory(tempDir.toString());
+ mavenPrj.setBuild(build);
+ mavenPrj.setDependencyArtifacts(artifacts);
+
+ PreparePackageMojo ppm = new PreparePackageMojo();
+ ppm.mavenSession = Mockito.mock(MavenSession.class);
+ ppm.project = mavenPrj;
+ ArchiverManager am = Mockito.mock(ArchiverManager.class);
+ UnArchiver ua = Mockito.mock(UnArchiver.class);
+ Mockito.when(am.getUnArchiver(Mockito.isA(File.class))).thenReturn(ua);
+ setPrivateField(ppm, "archiverManager", am);
+ setPrivateField(ppm, "artifactHandlerManager", ahm);
+ setPrivateField(ppm, "resolver", Mockito.mock(ArtifactResolver.class));
+ return ppm;
+ }
+
+ private org.apache.maven.artifact.Artifact getMavenArtifact(File repoRoot,
ArtifactHandler ah, String gid, String aid, String ver) {
+ return getMavenArtifact(repoRoot, ah, gid, aid, ver, null);
+ }
+
+ private org.apache.maven.artifact.Artifact getMavenArtifact(File repoRoot,
ArtifactHandler ah, String gid, String aid, String ver, String classifier) {
+ DefaultArtifact art = new DefaultArtifact(gid, aid, ver, "compile",
"jar", classifier, ah);
+ art.setFile(getMavenArtifactFile(repoRoot, gid, aid, ver));
+ return art;
+ }
+
+ private File getMavenArtifactFile(File repoRoot, String gid, String aid,
String ver) {
+ return new File(repoRoot, gid.replace('.', '/') + '/' + aid + '/' +
ver + '/' + aid + '-' + ver + ".jar");
+ }
+
+ private File getMavenRepoRoot() throws IOException {
+ URL res = getClass().getClassLoader().getResource(
+ Test.class.getName().replace('.', '/') + ".class");
+
+ String u = res.toExternalForm();
+ if (u.startsWith("jar:"))
+ u = u.substring(4);
+
+ int idx = u.indexOf("junit");
+ if (idx < 0)
+ throw new IllegalStateException("Cannot infer maven repo root: " +
res);
+
+ return new File(new URL(u.substring(0, idx)).getFile());
+ }
+
+ private void setPrivateField(Object obj, String name, Object val) throws
Exception {
+ Field f = obj.getClass().getDeclaredField(name);
+ f.setAccessible(true);
+ f.set(obj, val);
+ }
+}