blob: eed2d5e4d593dc8228f67785a9e36d7bbedf042c [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.servicemix.kernel.testing.support;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.felix.framework.Felix;
import org.apache.felix.framework.util.CompoundEnumeration;
import org.apache.felix.framework.util.FelixConstants;
import org.apache.servicemix.kernel.main.Main;
import org.apache.servicemix.kernel.main.spi.MainService;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.springframework.osgi.test.platform.FelixPlatform;
import org.springframework.osgi.test.platform.OsgiPlatform;
import org.springframework.util.ClassUtils;
public class SmxKernelPlatform implements OsgiPlatform {
private static final Log log = LogFactory.getLog(FelixPlatform.class);
private static final String FELIX_CONF_FILE = "config.properties";
private static final String FELIX_CONFIG_PROPERTY = "config.properties";
public final static String FRAMEWORK_STORAGE = "org.osgi.framework.storage";
private BundleContext context;
private Object platform;
private File felixStorageDir;
private Properties configurationProperties = new Properties();
protected Properties getPlatformProperties() {
// load Felix configuration
Properties props = new Properties();
props.putAll(getFelixConfiguration());
props.putAll(getLocalConfiguration());
return props;
}
public Properties getConfigurationProperties() {
// local properties
configurationProperties.putAll(getPlatformProperties());
// system properties
configurationProperties.putAll(System.getProperties());
return configurationProperties;
}
public BundleContext getBundleContext() {
return context;
}
private Set<String> getJars(Class... classes) {
Set<String> jars = new HashSet<String>();
for (Class cl : classes) {
String name = cl.getName().replace('.', '/') + ".class";
URL url = (cl.getClassLoader() != null ? cl.getClassLoader() : getClass().getClassLoader()).getResource(name);
String path = url.toString();
if (path.startsWith("jar:")) {
path = path.substring(0, path.indexOf('!'));
} else {
path = path.substring(0, path.indexOf(name));
}
jars.add(path);
}
return jars;
}
public void start() throws Exception {
Set<String> jars = getJars(Felix.class);
ClassLoader classLoader = new GuardClassLoader(toURLs(jars.toArray(new String[jars.size()])), null);
BundleActivator activator = new BundleActivator() {
private ServiceRegistration registration;
public void start(BundleContext context) {
registration = context.registerService(MainService.class.getName(), new MainService() {
public String[] getArgs() {
return new String[0];
}
public int getExitCode() {
return 0;
}
public void setExitCode(int exitCode) {
}
}, null);
}
public void stop(BundleContext context) {
registration.unregister();
}
};
List<BundleActivator> activations = new ArrayList<BundleActivator>();
activations.add(activator);
Properties props = getConfigurationProperties();
props.put(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP, activations);
Thread.currentThread().setContextClassLoader(classLoader);
Class cl = classLoader.loadClass(Felix.class.getName());
Constructor cns = cl.getConstructor(Map.class);
platform = cns.newInstance(props);
platform.getClass().getMethod("start").invoke(platform);
Bundle systemBundle = (Bundle) platform;
// call getBundleContext
final Method getContext = systemBundle.getClass().getMethod("getBundleContext", null);
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
getContext.setAccessible(true);
return null;
}
});
context = (BundleContext) getContext.invoke(systemBundle, null);
}
public void stop() throws Exception {
try {
platform.getClass().getMethod("stop").invoke(platform);
}
finally {
// remove cache folder
delete(felixStorageDir);
}
}
public String toString() {
return getClass().getName();
}
File createTempDir(String suffix) {
if (suffix == null)
suffix = "osgi";
File tempFileName;
try {
tempFileName = File.createTempFile("org.sfw.osgi", suffix);
}
catch (IOException ex) {
if (log.isWarnEnabled()) {
log.warn("Could not create temporary directory, returning a temp folder inside the current folder", ex);
}
return new File("./tmp-test");
}
tempFileName.delete(); // we want it to be a directory...
File tempFolder = new File(tempFileName.getAbsolutePath());
tempFolder.mkdir();
return tempFolder;
}
/**
* Configuration settings for the OSGi test run.
*
* @return
*/
private Properties getLocalConfiguration() {
Properties props = new Properties();
felixStorageDir = createTempDir("felix");
props.setProperty(FRAMEWORK_STORAGE, this.felixStorageDir.getAbsolutePath());
if (log.isTraceEnabled())
log.trace("felix storage dir is " + felixStorageDir.getAbsolutePath());
return props;
}
/**
* Loads Felix config.properties.
*
* <strong>Note</strong> the current implementation uses Felix's Main class
* to resolve placeholders as opposed to loading the properties manually
* (through JDK's Properties class or Spring's PropertiesFactoryBean).
*
* @return
*/
// TODO: this method should be removed once Felix 1.0.2 is released
private Properties getFelixConfiguration() {
String location = "/".concat(ClassUtils.classPackageAsResourcePath(getClass())).concat("/").concat(FELIX_CONF_FILE);
URL url = getClass().getResource(location);
if (url == null)
throw new RuntimeException("cannot find felix configuration properties file:" + location);
// used with Main
System.getProperties().setProperty(FELIX_CONFIG_PROPERTY, url.toExternalForm());
// load config.properties (use Felix's Main for resolving placeholders)
Properties props = new Properties();
InputStream is = null;
try {
is = url.openConnection().getInputStream();
props.load(is);
is.close();
}
catch (FileNotFoundException ex) {
// Ignore file not found.
}
catch (Exception ex) {
System.err.println("Main: Error loading system properties from " + url);
System.err.println("Main: " + ex);
try {
if (is != null) is.close();
}
catch (IOException ex2) {
// Nothing we can do.
}
return null;
}
// Perform variable substitution for system properties.
for (Enumeration e = props.propertyNames(); e.hasMoreElements();) {
String name = (String) e.nextElement();
props.setProperty(name, Main.substVars(props.getProperty(name), name, null, props));
}
return props;
}
/**
* Delete the given file (can be a simple file or a folder).
*
* @param file the file to be deleted
* @return if the deletion succeded or not
*/
public static boolean delete(File file) {
// bail out quickly
if (file == null)
return false;
// recursively delete children file
boolean success = true;
if (file.isDirectory()) {
String[] children = file.list();
for (int i = 0; i < children.length; i++) {
success &= delete(new File(file, children[i]));
}
}
// The directory is now empty so delete it
return (success &= file.delete());
}
private static URL[] toURLs(String[] jars) throws MalformedURLException {
URL[] urls = new URL[jars.length];
for (int i = 0; i < urls.length; i++) {
String s = jars[i];
if (s.startsWith("jar:")) {
s = s.substring("jar:".length());
}
urls[i] = new URL(s);
}
return urls;
}
public class GuardClassLoader extends URLClassLoader {
private Set<String> bootDelegationPackages = new HashSet<String>();
private Set<String> packages = new HashSet<String>();
private List<ClassLoader> parents = new ArrayList<ClassLoader>();
public GuardClassLoader(URL[] urls, List<String> additionalPackages) throws MalformedURLException {
super(urls, SmxKernelPlatform.class.getClassLoader());
Properties props = getConfigurationProperties();
String prop = props.getProperty("org.osgi.framework.system.packages");
String[] ps = prop.split(",");
for (String p : ps) {
String[] spack = p.split(";");
for (String sp : spack) {
sp = sp.trim();
if (!sp.startsWith("version")) {
packages.add(sp);
}
}
}
if (additionalPackages != null) {
packages.addAll(additionalPackages);
}
prop = props.getProperty("org.osgi.framework.bootdelegation");
ps = prop.split(",");
for (String p : ps) {
p = p.trim();
if (p.endsWith("*")) {
p = p.substring(0, p.length() - 1);
}
bootDelegationPackages.add(p);
}
ClassLoader cl = getParent();
while (cl != null) {
parents.add(0, cl);
cl = cl.getParent();
}
//System.err.println("Boot packages: " + packages);
}
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
//System.err.println("Loading class: " + name);
Class c = findLoadedClass(name);
if (c == null) {
String pkg = name.substring(0, name.lastIndexOf('.'));
boolean match = name.startsWith("java.") || packages.contains(pkg);
if (!match) {
for (String p : bootDelegationPackages) {
if (pkg.startsWith(p)) {
match = true;
break;
}
}
}
if (match) {
for (ClassLoader cl : parents) {
try {
c = cl.loadClass(name);
//System.err.println("Class loaded from: " + cl.getResource(name.replace('.', '/') + ".class"));
break;
} catch (ClassNotFoundException e) {
}
}
if (c == null) {
throw new ClassNotFoundException(name);
}
//c = getParent().loadClass(name);
} else {
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
public URL getResource(String name) {
//System.err.println("GetResource: " + name);
URL url = getParent().getResource(name);
if (url != null && url.toString().startsWith("file:")) {
return url;
}
url = findResource(name);
System.err.println("Resource " + name + " found at " + url);
return url;
/*
URL u = getParent().getResource(name);
if (u != null) {
String path = u.toString();
int idx = path.indexOf('!');
if (idx > 0) {
path = path.substring(0, idx);
if (!jars.contains(path)) {
return null;
}
} else {
idx = 0;
}
}
return u;
*/
}
public Enumeration<URL> getResources(final String name) throws IOException {
//System.err.println("GetResources: " + name);
Enumeration[] tmp = new Enumeration[2];
final Enumeration<URL> e = getParent().getResources(name);
tmp[0] = new Enumeration<URL>() {
URL next = null;
public boolean hasMoreElements() {
while (next == null && e.hasMoreElements()) {
next = e.nextElement();
String path = next.toString();
if (!path.startsWith("file:")) {
next = null;
}
}
return next != null;
}
public URL nextElement() {
return next;
}
};
tmp[1] = findResources(name);
return new CompoundEnumeration(tmp) {
public Object nextElement() {
Object next = super.nextElement();
System.err.println("Resources " + name + " found at " + next);
return next;
}
};
}
}
}