blob: 56cfdf70b05ef89541292f38ef6cda05e41369d9 [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.paxexam.util;
import static org.ops4j.pax.exam.CoreOptions.junitBundles;
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
import static org.ops4j.pax.exam.CoreOptions.systemProperty;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.apache.sling.maven.projectsupport.BundleListUtils;
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.ops4j.pax.exam.CoreOptions;
import org.ops4j.pax.exam.options.CompositeOption;
import org.ops4j.pax.exam.options.DefaultCompositeOption;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Pax exam options and utilities to test Sling applications
* The basic idea is to get a vanilla Sling launchpad instance
* setup with a minimal amount of boilerplate code.
* See SlingSetupTest for an example.
*/
public class SlingPaxOptions {
private static final Logger log = LoggerFactory.getLogger(SlingPaxOptions.class);
public static final int DEFAULT_SLING_START_LEVEL = 30;
public static final String PROP_TELNET_PORT = "osgi.shell.telnet.port";
public static final String PROP_HTTP_PORT = "org.osgi.service.http.port";
public static final String DEFAULT_RUN_MODES = "jackrabbit";
private static String [] ignoredBundlePrefixes;
private static int getAvailablePort() {
int result = Integer.MIN_VALUE;
try {
final ServerSocket s = new ServerSocket(0);
result = s.getLocalPort();
s.close();
} catch(IOException ignore) {
}
return result;
}
/**
* When reading bundle lists, ignore bundles which have symbolic names
* starting with one of the supplied prefixes
* @param symbolicNamePrefix Symbolic name prefixes
*/
public static void setIgnoredBundles(String ... symbolicNamePrefix) {
if(symbolicNamePrefix == null || symbolicNamePrefix.length == 0) {
ignoredBundlePrefixes = new String[] {};
} else {
ignoredBundlePrefixes = symbolicNamePrefix;
}
}
/**
* Get run modes to use for our tests, as set by the sling.run.modes property
* @return Run modes
*/
public static Collection<String> getTestRunModes() {
final String runModes = System.getProperty("sling.run.modes", DEFAULT_RUN_MODES);
return Arrays.asList(runModes.split(","));
}
/**
* Set default launchpad options
* @param launchpadVersion null means use the latest
* @return Composite option
*/
public static CompositeOption defaultLaunchpadOptions(String launchpadVersion) {
final String paxLogLevel = System.getProperty("pax.exam.log.level", "INFO");
final int slingStartLevel = DEFAULT_SLING_START_LEVEL;
final String telnetPort = System.getProperty(PROP_TELNET_PORT, String.valueOf(getAvailablePort()));
final String httpPort = System.getProperty(PROP_HTTP_PORT, String.valueOf(getAvailablePort()));
log.info("{}={}", PROP_TELNET_PORT, telnetPort);
log.info("{}={}", PROP_HTTP_PORT, httpPort);
return new DefaultCompositeOption(
junitBundles(),
systemProperty( "org.ops4j.pax.logging.DefaultServiceLog.level" ).value(paxLogLevel),
SlingPaxOptions.felixRemoteShellBundles(),
SlingPaxOptions.slingBootstrapBundles(),
SlingPaxOptions.slingLaunchpadBundles(launchpadVersion),
CoreOptions.frameworkStartLevel(slingStartLevel),
CoreOptions.frameworkProperty(PROP_TELNET_PORT).value(telnetPort),
CoreOptions.frameworkProperty(PROP_HTTP_PORT).value(httpPort)
);
}
private static boolean ignore(Bundle b) {
boolean result = false;
if(ignoredBundlePrefixes != null) {
final String sn = b.getArtifactId();
for(String prefix : ignoredBundlePrefixes) {
if(sn.startsWith(prefix)) {
result = true;
break;
}
}
}
return result;
}
public static CompositeOption slingBundleList(String groupId, String artifactId, String version, String type, String classifier) {
final DefaultCompositeOption result = new DefaultCompositeOption();
final String paxUrl = new StringBuilder()
.append("mvn:")
.append(groupId)
.append("/")
.append(artifactId)
.append("/")
.append(version == null ? "" : version)
.append("/")
.append(type == null ? "" : type)
.append("/")
.append(classifier == null ? "" : classifier)
.toString();
// TODO BundleList should take an InputStream - for now copy to a tmp file for parsing
log.info("Getting bundle list {}", paxUrl);
File tmp = null;
final Collection<String> testRunModes = getTestRunModes();
try {
tmp = dumpMvnUrlToTmpFile(paxUrl);
final BundleList list = BundleListUtils.readBundleList(tmp);
int counter = 0;
int ignored = 0;
for(StartLevel s : list.getStartLevels()) {
// Start level < 0 means bootstrap in our bundle lists
final int startLevel = s.getStartLevel() < 0 ? 1 : s.getStartLevel();
for(Bundle b : s.getBundles()) {
if(ignore(b)) {
log.info("Bundle ignored due to setIgnoredBundles: {}", b);
ignored++;
continue;
}
counter++;
// TODO need better fragment detection
// (but pax exam should really detect that by itself?)
final List<String> KNOWN_FRAGMENTS = new ArrayList<String>();
KNOWN_FRAGMENTS.add("org.apache.sling.extensions.webconsolebranding");
final boolean isFragment = b.getArtifactId().contains("fragment") || KNOWN_FRAGMENTS.contains(b.getArtifactId());
// Ignore bundles with run modes that do not match ours
final String bundleRunModes = b.getRunModes();
if(bundleRunModes != null && bundleRunModes.length() > 0) {
boolean active = false;
for(String m : bundleRunModes.split(",")) {
if(testRunModes.contains(m)) {
active = true;
break;
}
}
if(!active) {
log.info("Ignoring bundle {} as none of its run modes [{}] are active in this test run {}",
new Object[] { b.getArtifactId(), bundleRunModes, testRunModes} );
continue;
}
}
if(isFragment) {
result.add(mavenBundle(b.getGroupId(), b.getArtifactId(), b.getVersion()).noStart());
} else if(startLevel == 0){
result.add(mavenBundle(b.getGroupId(), b.getArtifactId(), b.getVersion()));
} else {
result.add(mavenBundle(b.getGroupId(), b.getArtifactId(), b.getVersion()).startLevel(startLevel));
}
log.info("Bundle added: {}/{}/{}", new Object [] { b.getGroupId(), b.getArtifactId(), b.getVersion()});
}
}
log.info("Got {} bundles ({} ignored) from {}", new Object[] { counter, ignored, paxUrl });
} catch(Exception e) {
throw new RuntimeException("Error getting bundle list " + paxUrl, e);
} finally {
if(tmp != null) {
tmp.delete();
}
}
return result;
}
public static CompositeOption slingBootstrapBundles() {
return new DefaultCompositeOption(
mavenBundle("org.apache.felix", "org.apache.felix.http.jetty", "2.2.0"),
// TODO: why is this needed?
mavenBundle("org.apache.sling", "org.apache.sling.launchpad.api", "1.1.0")
);
}
public static CompositeOption slingLaunchpadBundles(String version) {
return slingBundleList("org.apache.sling", "org.apache.sling.launchpad", version, "xml", "bundlelist");
}
/**
* Felix remote shell bundles
* @return Composite option
*/
public static CompositeOption felixRemoteShellBundles() {
final String gogoVersion = "0.10.0";
return new DefaultCompositeOption(
mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.gogo.runtime").version(gogoVersion),
mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.gogo.shell").version(gogoVersion),
mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.gogo.command").version(gogoVersion),
mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.shell.remote").version("1.1.2")
);
}
private static File dumpMvnUrlToTmpFile(String mvnUrl) throws IOException {
final URL url = new URL(mvnUrl);
final InputStream is = new BufferedInputStream(url.openStream());
final File tmp = File.createTempFile(SlingPaxOptions.class.getName(), "xml");
log.debug("Copying bundle list contents to {}", tmp.getAbsolutePath());
tmp.deleteOnExit();
final OutputStream os = new BufferedOutputStream(new FileOutputStream(tmp));
try {
final byte [] buffer = new byte[16384];
int len = 0;
while( (len = is.read(buffer, 0, buffer.length)) > 0) {
os.write(buffer, 0, len);
}
os.flush();
} finally {
os.close();
is.close();
}
return tmp;
}
}