blob: 045fe681e74422f87e5644410bd5762c8de315af [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.tuscany.sca.node.equinox.launcher;
import static java.lang.System.currentTimeMillis;
import static java.lang.System.setProperty;
import static org.apache.tuscany.sca.node.equinox.launcher.NodeLauncherUtil.GATEWAY_BUNDLE;
import static org.apache.tuscany.sca.node.equinox.launcher.NodeLauncherUtil.LAUNCHER_EQUINOX_LIBRARIES;
import static org.apache.tuscany.sca.node.equinox.launcher.NodeLauncherUtil.artifactId;
import static org.apache.tuscany.sca.node.equinox.launcher.NodeLauncherUtil.bundleName;
import static org.apache.tuscany.sca.node.equinox.launcher.NodeLauncherUtil.file;
import static org.apache.tuscany.sca.node.equinox.launcher.NodeLauncherUtil.fixupBundle;
import static org.apache.tuscany.sca.node.equinox.launcher.NodeLauncherUtil.jarVersion;
import static org.apache.tuscany.sca.node.equinox.launcher.NodeLauncherUtil.runtimeClasspathEntries;
import static org.apache.tuscany.sca.node.equinox.launcher.NodeLauncherUtil.string;
import static org.apache.tuscany.sca.node.equinox.launcher.NodeLauncherUtil.thirdPartyLibraryBundle;
import static org.apache.tuscany.sca.node.equinox.launcher.NodeLauncherUtil.thisBundleLocation;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.launch.Framework;
/**
* Wraps the Equinox runtime.
*/
public class EquinoxHost {
static final String PROP_OSGI_CONTEXT_CLASS_LOADER_PARENT = "osgi.contextClassLoaderParent";
static final String PROP_OSGI_CLEAN = "osgi.clean";
static final String PROP_USER_NAME = "user.name";
private static Logger logger = Logger.getLogger(EquinoxHost.class.getName());
static final String PROP_INSTALL_AREA = "osgi.install.area";
static final String PROP_CONFIG_AREA = "osgi.configuration.area";
static final String PROP_CONFIG_AREA_DEFAULT = "osgi.configuration.area.default";
static final String PROP_SHARED_CONFIG_AREA = "osgi.sharedConfiguration.area";
static final String PROP_INSTANCE_AREA = "osgi.instance.area";
static final String PROP_INSTANCE_AREA_DEFAULT = "osgi.instance.area.default";
static final String PROP_USER_AREA = "osgi.user.area";
static final String PROP_USER_AREA_DEFAULT = "osgi.user.area.default";
/**
* If the class is loaded inside OSGi, then the bundle context will be injected by the activator
*/
static BundleContext injectedBundleContext;
static {
if (getSystemProperty("osgi.debug") != null) {
logger.setLevel(Level.FINE);
}
}
private BundleContext bundleContext;
private Bundle launcherBundle;
private List<URL> bundleFiles = new ArrayList<URL>();
private List<String> bundleNames = new ArrayList<String>();
private Map<URL, Manifest> jarFiles = new HashMap<URL, Manifest>();
private Map<String, Bundle> allBundles = new HashMap<String, Bundle>();
private List<Bundle> installedBundles = new ArrayList<Bundle>();
private Set<URL> bundleLocations;
private boolean aggregateThirdPartyJars = false;
private FrameworkLauncher frameworkLauncher = new FrameworkLauncher();
private Framework framework;
public EquinoxHost() {
super();
}
public EquinoxHost(Set<URL> urls) {
super();
this.bundleLocations = urls;
}
private static String getSystemProperty(final String name) {
return AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty(name);
}
});
}
private static Properties getSystemProperties() {
return AccessController.doPrivileged(new PrivilegedAction<Properties>() {
public Properties run() {
Properties props = new Properties();
for (Map.Entry<Object, Object> e : System.getProperties().entrySet()) {
if (e.getKey() instanceof String) {
String prop = (String)e.getKey();
if (prop.startsWith("osgi.") || prop.startsWith("eclipse.")) {
props.put(prop, e.getValue());
}
}
}
return props;
}
});
}
private static void put(Properties props, String key, String value) {
if (!props.contains(key)) {
props.put(key, value);
}
}
/**
* Search for org/apache/tuscany/sca/node/equinox/launcher for customized MANIFEST.MF
* for a given artifact. For example, a-1.0.MF for a-1.0.jar.
*
* @param fileName
* @return
* @throws IOException
*/
private Manifest getCustomizedMF(String fileName) throws IOException {
int index = fileName.lastIndexOf('.');
if (index == -1) {
return null;
}
String mf = fileName.substring(0, index) + ".MF";
InputStream is = getClass().getResourceAsStream(mf);
if (is == null) {
return null;
} else {
try {
Manifest manifest = new Manifest(is);
return manifest;
} finally {
is.close();
}
}
}
/**
* Start the Equinox host.
*
* @return
*/
public BundleContext start() {
try {
if (injectedBundleContext == null) {
Properties props = configureProperties();
startFramework(props);
} else {
// Get bundle context from the running Eclipse instance
bundleContext = injectedBundleContext;
}
// Determine the runtime classpath entries
Set<URL> urls;
urls = findBundleLocations();
// Sort out which are bundles (and not already installed) and which are just
// regular JARs
for (URL url : urls) {
File file = file(url);
Manifest manifest = getCustomizedMF(file.getName());
String bundleName = null;
if (manifest == null) {
bundleName = bundleName(file);
} else {
if (manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME) == null) {
manifest = null;
}
}
if (bundleName != null) {
bundleFiles.add(url);
bundleNames.add(bundleName);
} else {
if (file.isFile()) {
jarFiles.put(url, manifest);
}
}
}
// Get the already installed bundles
for (Bundle bundle : bundleContext.getBundles()) {
allBundles.put(bundle.getSymbolicName(), bundle);
}
// Install the launcher bundle if necessary
String launcherBundleName = "org.apache.tuscany.sca.node.launcher.equinox";
String launcherBundleLocation;
launcherBundle = allBundles.get(launcherBundleName);
if (launcherBundle == null) {
launcherBundleLocation = thisBundleLocation();
if (logger.isLoggable(Level.FINE)) {
logger.fine("Installing launcher bundle: " + launcherBundleLocation);
}
fixupBundle(launcherBundleLocation);
launcherBundle = bundleContext.installBundle(launcherBundleLocation);
allBundles.put(launcherBundleName, launcherBundle);
installedBundles.add(launcherBundle);
} else {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Launcher bundle is already installed: " + string(launcherBundle, false));
}
// launcherBundleLocation = thisBundleLocation(launcherBundle);
}
// FIXME: SDO bundles dont have the correct dependencies
setProperty("commonj.sdo.impl.HelperProvider", "org.apache.tuscany.sdo.helper.HelperProviderImpl");
// Install the Tuscany bundles
long start = currentTimeMillis();
if (logger.isLoggable(Level.FINE)) {
logger.fine("Generating third-party library bundle.");
}
logger.info("Checking for manfiests customized by Tuscany in node-launcher-equinox/resources");
long libraryStart = currentTimeMillis();
Set<String> serviceProviders = new HashSet<String>();
if (!aggregateThirdPartyJars) {
for (Map.Entry<URL, Manifest> entry : jarFiles.entrySet()) {
URL jarFile = entry.getKey();
Manifest manifest = entry.getValue();
Bundle bundle = null;
if (manifest == null) {
bundle = installAsBundle(jarFile, null);
} else {
bundle = installAsBundle(Collections.singleton(jarFile), manifest);
}
isServiceProvider(bundle, serviceProviders);
}
} else {
Bundle bundle = installAsBundle(jarFiles.keySet(), LAUNCHER_EQUINOX_LIBRARIES);
isServiceProvider(bundle, serviceProviders);
}
installGatewayBundle(serviceProviders);
if (logger.isLoggable(Level.FINE)) {
logger
.fine("Third-party library bundle installed in " + (currentTimeMillis() - libraryStart) + " ms: ");
}
// Install all the other bundles that are not already installed
for (URL bundleFile : bundleFiles) {
fixupBundle(bundleFile.toString());
}
for (int i = 0, n = bundleFiles.size(); i < n; i++) {
URL bundleFile = bundleFiles.get(i);
String bundleName = bundleNames.get(i);
if (bundleName.contains("org.eclipse.jdt.junit") || bundleName.contains("org.apache.tuscany.sca.base")) {
continue;
}
installBundle(bundleFile, bundleName);
}
long end = currentTimeMillis();
if (logger.isLoggable(Level.FINE)) {
logger.fine("Tuscany bundles are installed in " + (end - start) + " ms.");
}
// Start the extensiblity and launcher bundles
String extensibilityBundleName = "org.apache.tuscany.sca.extensibility.equinox";
Bundle extensibilityBundle = allBundles.get(extensibilityBundleName);
if (extensibilityBundle != null) {
if ((extensibilityBundle.getState() & Bundle.ACTIVE) == 0) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Starting bundle: " + string(extensibilityBundle, false));
}
extensibilityBundle.start();
} else if (logger.isLoggable(Level.FINE)) {
logger.fine("Bundle is already started: " + string(extensibilityBundle, false));
}
}
if ((launcherBundle.getState() & Bundle.ACTIVE) == 0) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Starting launcher bundle: " + string(launcherBundle, false));
}
launcherBundle.start();
} else if (logger.isLoggable(Level.FINE)) {
logger.fine("Bundle is already started: " + string(launcherBundle, false));
}
// Start all our bundles for now to help diagnose any class loading issues
// startBundles( bundleContext );
return bundleContext;
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
protected Properties configureProperties() throws IOException, FileNotFoundException {
String version = getSystemProperty("java.specification.version");
/**
* [rfeng] I have to remove javax.transaction.* packages from the system bundle
* See: http://www.mail-archive.com/dev@geronimo.apache.org/msg70761.html
*/
String profile = "J2SE-1.5.profile";
if (version.startsWith("1.6")) {
profile = "JavaSE-1.6.profile";
}
if (version.startsWith("1.7")) {
profile = "JavaSE-1.7.profile";
}
Properties props = new Properties();
InputStream is = getClass().getResourceAsStream(profile);
if (is != null) {
props.load(is);
is.close();
}
props.putAll(getSystemProperties());
// Configure Eclipse properties
// Use the boot classloader as the parent classloader
put(props, PROP_OSGI_CONTEXT_CLASS_LOADER_PARENT, "app");
// Set startup properties
put(props, PROP_OSGI_CLEAN, "true");
// Set location properties
// FIXME Use proper locations
String tmpDir = getSystemProperty("java.io.tmpdir");
File root = new File(tmpDir);
// Add user name as the prefix. For multiple users on the same Lunix,
// there will be permission issue if one user creates the .tuscany folder
// first under /tmp with no write permission for others.
String userName = getSystemProperty(PROP_USER_NAME);
if (userName != null) {
root = new File(root, userName);
}
root = new File(root, ".tuscany/equinox/" + UUID.randomUUID().toString());
if (logger.isLoggable(Level.FINE)) {
logger.fine("Equinox location: " + root);
}
put(props, PROP_INSTANCE_AREA_DEFAULT, new File(root, "workspace").toURI().toString());
put(props, PROP_INSTALL_AREA, new File(root, "install").toURI().toString());
put(props, PROP_CONFIG_AREA_DEFAULT, new File(root, "config").toURI().toString());
put(props, PROP_USER_AREA_DEFAULT, new File(root, "user").toURI().toString());
// Test if the configuration/config.ini or osgi.bundles has been set
// If yes, try to avoid discovery of bundles
if (bundleLocations == null) {
if (props.getProperty("osgi.bundles") != null) {
bundleLocations = Collections.emptySet();
} else {
String config = props.getProperty(PROP_CONFIG_AREA);
File ini = new File(config, "config.ini");
if (ini.isFile()) {
Properties iniProps = new Properties();
iniProps.load(new FileInputStream(ini));
if (iniProps.getProperty("osgi.bundles") != null) {
bundleLocations = Collections.emptySet();
}
}
}
}
return props;
}
private boolean isServiceProvider(Bundle bundle, Set<String> serviceProviders) {
if (bundle != null) {
String export = (String)bundle.getHeaders().get(Constants.EXPORT_PACKAGE);
if (export != null && export.contains(NodeLauncherUtil.META_INF_SERVICES)) {
serviceProviders.add(bundle.getSymbolicName());
return true;
}
}
return false;
}
private void installGatewayBundle(Set<String> bundles) throws IOException, BundleException {
if (allBundles.containsKey(GATEWAY_BUNDLE)) {
return;
}
if (bundles == null) {
bundles = allBundles.keySet();
}
InputStream gateway = NodeLauncherUtil.generateGatewayBundle(bundles, null, false);
if (gateway != null) {
Bundle gatewayBundle = bundleContext.installBundle(GATEWAY_BUNDLE, gateway);
allBundles.put(NodeLauncherUtil.GATEWAY_BUNDLE, gatewayBundle);
installedBundles.add(gatewayBundle);
}
}
/**
* Start all the bundles as a check for class loading issues
* @param bundleContext - the bundle context
*/
private void startBundles(BundleContext bundleContext) {
for (Bundle bundle : bundleContext.getBundles()) {
// if (bundle.getSymbolicName().startsWith("org.apache.tuscany.sca")) {
if ((bundle.getState() & Bundle.ACTIVE) == 0) {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Starting bundle: " + string(bundle, false));
} // end if
try {
bundle.start();
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
// throw e;
} // end try
if (logger.isLoggable(Level.FINE)) {
logger.fine("Bundle: " + string(bundle, false));
} // end if
} // end if
// } // end if
} // end for
logger.fine("Tuscany bundles are started.");
return;
} // end startBundles
public Bundle installAsBundle(Collection<URL> jarFiles, String libraryBundleName) throws IOException,
BundleException {
// Install a single 'library' bundle for the third-party JAR files
Bundle libraryBundle = allBundles.get(libraryBundleName);
if (libraryBundle == null) {
InputStream library = thirdPartyLibraryBundle(jarFiles, libraryBundleName, null);
libraryBundle = bundleContext.installBundle(libraryBundleName, library);
allBundles.put(libraryBundleName, libraryBundle);
installedBundles.add(libraryBundle);
} else {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Third-party library bundle is already installed: " + string(libraryBundle, false));
}
}
return libraryBundle;
}
public Bundle installAsBundle(Collection<URL> jarFiles, Manifest manifest) throws IOException, BundleException {
String bundleName = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
// Install a single 'library' bundle for the third-party JAR files
Bundle libraryBundle = allBundles.get(bundleName);
if (libraryBundle == null) {
InputStream library = thirdPartyLibraryBundle(jarFiles, manifest);
libraryBundle = bundleContext.installBundle(bundleName, library);
allBundles.put(bundleName, libraryBundle);
installedBundles.add(libraryBundle);
} else {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Third-party library bundle is already installed: " + string(libraryBundle, false));
}
}
return libraryBundle;
}
public Bundle installBundle(URL bundleFile, String bundleName) throws MalformedURLException, BundleException {
if (bundleName == null) {
try {
bundleName = bundleName(file(bundleFile));
} catch (IOException e) {
bundleName = null;
}
}
Bundle bundle = allBundles.get(bundleName);
if (bundle == null) {
long installStart = currentTimeMillis();
String location = bundleFile.toString();
if (frameworkLauncher.isEquinox() && "file".equals(bundleFile.getProtocol())) {
File target = file(bundleFile);
// Use a special "reference" scheme to install the bundle as a reference
// instead of copying the bundle
location = "reference:file:/" + target.getPath();
}
bundle = bundleContext.installBundle(location);
if (logger.isLoggable(Level.FINE)) {
logger.fine("Bundle " + bundleFile + " installed in " + (currentTimeMillis() - installStart)
+ " ms: "
+ string(bundle, false));
}
allBundles.put(bundleName, bundle);
installedBundles.add(bundle);
}
return bundle;
}
public Bundle installAsBundle(URL jarFile, String symbolicName) throws IOException, BundleException {
if (symbolicName == null) {
symbolicName = LAUNCHER_EQUINOX_LIBRARIES + "." + artifactId(jarFile);
}
Bundle bundle = allBundles.get(symbolicName);
if (bundle == null) {
String version = jarVersion(jarFile);
if (logger.isLoggable(Level.FINE)) {
logger.fine("Installing third-party jar as bundle: " + jarFile);
}
InputStream is = thirdPartyLibraryBundle(Collections.singleton(jarFile), symbolicName, version);
// try/catch and output message added 10/04/2009 Mike Edwards
try {
bundle = bundleContext.installBundle(symbolicName, is);
allBundles.put(symbolicName, bundle);
installedBundles.add(bundle);
} catch (BundleException e) {
System.out
.println("EquinoxHost:installAsBundle - BundleException raised when dealing with jar " + symbolicName);
throw (e);
} // end try
// end of addition
}
return bundle;
}
private Set<URL> findBundleLocations() throws FileNotFoundException, URISyntaxException, MalformedURLException {
if (bundleLocations == null ||
(bundleLocations != null && bundleLocations.size() == 0)) {
if (injectedBundleContext != null) {
// Use classpath entries from a distribution if there is one and the modules
// directories available in a dev environment for example
bundleLocations = runtimeClasspathEntries(true, false, true);
} else {
// Use classpath entries from a distribution if there is one and the classpath
// entries on the current application's classloader
// *** Changed by Mike Edwards, 9th April 2009 ***
// -- this place is reached when starting from within Eclipse so why use the classpath??
// bundleLocations = runtimeClasspathEntries(true, true, false);
// Instead search the modules directory
bundleLocations = runtimeClasspathEntries(true, true, true);
}
}
return bundleLocations;
}
/**
* Stop the Equinox host.
*/
public void stop() {
try {
// Uninstall all the bundles we've installed
for (int i = installedBundles.size() - 1; i >= 0; i--) {
Bundle bundle = installedBundles.get(i);
try {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Uninstalling bundle: " + string(bundle, false));
}
bundle.uninstall();
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage(), e);
}
}
installedBundles.clear();
stopFramework();
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
/*
private void startFramework(Properties props) throws Exception {
EclipseStarter.setInitialProperties(props);
// Test if the configuration/config.ini or osgi.bundles has been set
// If yes, try to avoid discovery of bundles
if (bundleLocations == null) {
if (props.getProperty("osgi.bundles") != null) {
bundleLocations = Collections.emptySet();
} else {
String config = props.getProperty(PROP_CONFIG_AREA);
File ini = new File(config, "config.ini");
if (ini.isFile()) {
Properties iniProps = new Properties();
iniProps.load(new FileInputStream(ini));
if (iniProps.getProperty("osgi.bundles") != null) {
bundleLocations = Collections.emptySet();
}
}
}
}
// Start Eclipse
bundleContext = EclipseStarter.startup(new String[] {}, null);
}
private void stopFramework() throws Exception {
// Shutdown Eclipse if we started it ourselves
if (injectedBundleContext == null) {
EclipseStarter.shutdown();
}
}
*/
private void startFramework(Map configuration) throws Exception {
if (framework != null) {
throw new IllegalStateException("The OSGi framework has been started");
}
framework = frameworkLauncher.newFramework(configuration);
framework.start();
this.bundleContext = framework.getBundleContext();
}
private void stopFramework() throws Exception {
// Shutdown Eclipse if we started it ourselves
if (injectedBundleContext == null) {
framework.stop();
framework.waitForStop(5000);
framework = null;
bundleContext = null;
}
}
public void setBundleLocations(Set<URL> bundleLocations) {
this.bundleLocations = bundleLocations;
}
}