blob: ad2d3fce8a21281db2a7c9ade7016b18522ab63f [file] [log] [blame]
/*
* 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.FileInputStream;
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.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
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 testBSNRenaming() 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.johnzon/1.0.0");
try {
String modelTxt = "[feature name=:launchpad]\n" +
"[artifacts]\n" +
" org.apache.sling/org.apache.sling.commons.classloader/1.3.2\n" +
"" +
"[feature name=rename_test]\n" +
" org.apache.sling/org.apache.sling.commons.johnzon/1.0.0 [bundle:rename-bsn=r-foo.bar.renamed.sling.commons.johnzon]\n";
Model model = ModelReader.read(new StringReader(modelTxt), null);
ppm.execute(model);
File orgJar = getMavenArtifactFile(getMavenRepoRoot(), "org.apache.sling", "org.apache.sling.commons.johnzon", "1.0.0");
File generatedJar = new File(ppm.getTmpDir() + "/r-foo.bar.renamed.sling.commons.johnzon-1.0.0.jar");
compareJarContents(orgJar, generatedJar);
try (JarFile jfOrg = new JarFile(orgJar);
JarFile jfNew = new JarFile(generatedJar)) {
Manifest mfOrg = jfOrg.getManifest();
Manifest mfNew = jfNew.getManifest();
Attributes orgAttrs = mfOrg.getMainAttributes();
Attributes newAttrs = mfNew.getMainAttributes();
for (Object key : orgAttrs.keySet()) {
String orgVal = orgAttrs.getValue(key.toString());
String newVal = newAttrs.getValue(key.toString());
if ("Bundle-SymbolicName".equals(key.toString())) {
assertEquals("Should have recorded the original Bundle-SymbolicName",
orgVal, newAttrs.getValue("X-Original-Bundle-SymbolicName"));
assertEquals("r-foo.bar.renamed.sling.commons.johnzon", newVal);
} else {
assertEquals("Different keys: " + key, orgVal, newVal);
}
}
}
} finally {
FileUtils.deleteDirectory(new File(ppm.project.getBuild().getDirectory()));
}
}
private static void compareJarContents(File orgJar, File actualJar) throws IOException {
try (JarInputStream jis1 = new JarInputStream(new FileInputStream(orgJar));
JarInputStream jis2 = new JarInputStream(new FileInputStream(actualJar))) {
JarEntry je1 = null;
while ((je1 = jis1.getNextJarEntry()) != null) {
if (je1.isDirectory())
continue;
JarEntry je2 = null;
while((je2 = jis2.getNextJarEntry()) != null) {
if (!je2.isDirectory())
break;
}
assertEquals(je1.getName(), je2.getName());
assertEquals(je1.getSize(), je2.getSize());
try {
byte[] buf1 = IOUtils.toByteArray(jis1);
byte[] buf2 = IOUtils.toByteArray(jis2);
assertArrayEquals("Contents not equal: " + je1.getName(), buf1, buf2);
} finally {
jis1.closeEntry();
jis2.closeEntry();
}
}
}
}
@Test
public void testSubsystemBaseGeneration() 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.johnzon/1.0.0",
"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.johnzon/1.0.0\n" +
" org.apache.sling/org.apache.sling.commons.mime/2.1.8\n" +
"" +
"[artifacts startLevel=20 runModes=foo,bar,:blah]\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.johnzon-1.0.0.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.johnzon", "1.0.0");
ZipEntry sjZE = jf.getEntry("Potential_Bundles/10/org.apache.sling.commons.johnzon-1.0.0.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);
}
}