diff options
Diffstat (limited to '')
-rw-r--r-- | tags/java/sca/1.5/modules/node-launcher-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/launcher/LauncherBundleActivator.java | 475 |
1 files changed, 475 insertions, 0 deletions
diff --git a/tags/java/sca/1.5/modules/node-launcher-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/launcher/LauncherBundleActivator.java b/tags/java/sca/1.5/modules/node-launcher-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/launcher/LauncherBundleActivator.java new file mode 100644 index 0000000000..afd6804ad2 --- /dev/null +++ b/tags/java/sca/1.5/modules/node-launcher-osgi/src/main/java/org/apache/tuscany/sca/node/osgi/launcher/LauncherBundleActivator.java @@ -0,0 +1,475 @@ +/* + * 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.osgi.launcher; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.net.URL; +import java.security.CodeSource; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.ZipEntry; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.BundleListener; +import org.osgi.framework.Constants; + +/** + * Bundle activator which installs Tuscany modules and 3rd party jars into an OSGi runtime. + * + */ +public class LauncherBundleActivator implements BundleActivator, Constants, BundleListener { + private static Logger logger = Logger.getLogger(LauncherBundleActivator.class.getName()); + private static final String[] immutableJars = {"bcprov"}; + + private BundleContext bundleContext; + private List<Bundle> tuscanyBundles = new ArrayList<Bundle>(); + + private List<URL> jarFiles; + + public LauncherBundleActivator() { + super(); + } + + public LauncherBundleActivator(List<URL> jarFiles) { + super(); + this.jarFiles = jarFiles; + } + + public static String toString(Bundle b, boolean verbose) { + StringBuffer sb = new StringBuffer(); + sb.append(b.getBundleId()).append(" ").append(b.getSymbolicName()); + int s = b.getState(); + if ((s & Bundle.UNINSTALLED) != 0) { + sb.append(" UNINSTALLED"); + } + if ((s & Bundle.INSTALLED) != 0) { + sb.append(" INSTALLED"); + } + if ((s & Bundle.RESOLVED) != 0) { + sb.append(" RESOLVED"); + } + if ((s & Bundle.STARTING) != 0) { + sb.append(" STARTING"); + } + if ((s & Bundle.STOPPING) != 0) { + sb.append(" STOPPING"); + } + if ((s & Bundle.ACTIVE) != 0) { + sb.append(" ACTIVE"); + } + + if (verbose) { + sb.append(" ").append(b.getLocation()); + sb.append(" ").append(b.getHeaders()); + } + return sb.toString(); + } + + public void start(BundleContext bundleContext) throws Exception { + this.bundleContext = bundleContext; + this.bundleContext.addBundleListener(this); + installTuscany(bundleContext); + + for (Bundle b : bundleContext.getBundles()) { + try { + if ("org.apache.tuscany.sca.contribution.osgi".equals(b.getSymbolicName())) { + b.start(); + logger.info(toString(b, false) + " " + b.getState()); + break; + } + } catch (Exception e) { + logger.log(Level.SEVERE, e.getMessage(), e); + } + } + } + + public void stop(BundleContext bundleContext) throws Exception { + /* + for (Bundle bundle : tuscanyBundles) { + try { + bundle.stop(); + } catch (Exception e) { + logger.log(Level.SEVERE, e.getMessage(), e); + } + } + */ + + for (Bundle bundle : tuscanyBundles) { + try { + if (logger.isLoggable(Level.FINE)) { + logger.fine("Uninstalling bundle: " + toString(bundle, false)); + } + bundle.uninstall(); + } catch (Exception e) { + logger.log(Level.SEVERE, e.getMessage(), e); + } + } + this.bundleContext.removeBundleListener(this); + tuscanyBundles.clear(); + this.bundleContext = null; + } + + public void installTuscany(BundleContext bundleContext) { + long start = System.currentTimeMillis(); + + try { + + // FIXME: SDO bundles dont have the correct dependencies + System.setProperty("commonj.sdo.impl.HelperProvider", "org.apache.tuscany.sdo.helper.HelperProviderImpl"); + List<URL> urls = jarFiles; + if (urls == null) { + File tuscanyInstallDir = findTuscanyInstallDir(bundleContext.getBundle()); + + urls = JarFileFinder.findJarFiles(tuscanyInstallDir, new JarFileFinder.StandAloneJARFileNameFilter()); + } + + for (URL url : urls) { + File file = new File(url.toURI()); + if (file.getName().startsWith("org.apache.felix.") || file.getName().startsWith("org.osgi.")) { + continue; + } + try { + Bundle bundle = createAndInstallBundle(bundleContext, url); + } catch (Exception e) { + logger.log(Level.SEVERE, e.getMessage(), e); + } + } + + long end = System.currentTimeMillis(); + logger.info("Tuscany bundles are installed in " + (end - start) + " ms."); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + private File findTuscanyInstallDir(Bundle bundle) throws IOException { + String tuscanyDirName = JarFileFinder.getProperty(JarFileFinder.TUSCANY_HOME); + if (tuscanyDirName != null) { + File tuscanyInstallDir = new File(tuscanyDirName); + if (tuscanyInstallDir.exists() && tuscanyInstallDir.isDirectory()) + return tuscanyInstallDir; + } + + String location = bundle.getLocation(); + + if (location != null && location.startsWith("file:")) { + File file = new File(URI.create(location)); + File tuscanyInstallDir = file.getParentFile(); + if (tuscanyInstallDir.exists() && tuscanyInstallDir.isDirectory()) + return tuscanyInstallDir; + } + if (this.getClass().getProtectionDomain() != null) { + CodeSource codeSource = this.getClass().getProtectionDomain().getCodeSource(); + if (codeSource != null) { + try { + File tuscanyInstallDir = new File(codeSource.getLocation().toURI()); + if (tuscanyInstallDir.exists() && tuscanyInstallDir.isDirectory()) + return tuscanyInstallDir; + } catch (Exception e) { + // ignore + } + } + } + return null; + } + + public Bundle createAndInstallBundle(BundleContext bundleContext, URL bundleFile) throws Exception { + if (logger.isLoggable(Level.FINE)) { + logger.fine("Installing bundle: " + bundleFile); + } + long start = System.currentTimeMillis(); + + Manifest manifest = readManifest(bundleFile); + boolean isOSGiBundle = manifest != null && manifest.getMainAttributes().getValue(BUNDLE_SYMBOLICNAME) != null; + + if (!isOSGiBundle) { + manifest = updateBundleManifest(bundleFile, manifest); + } + + String symbolicName = manifest.getMainAttributes().getValue(BUNDLE_SYMBOLICNAME); + String version = manifest.getMainAttributes().getValue(BUNDLE_VERSION); + Bundle bundle = findBundle(bundleContext, symbolicName, version); + if (bundle != null) { + if (logger.isLoggable(Level.FINE)) { + logger.fine("Bundle is already installed: " + symbolicName); + } + return bundle; + } + + String bundleLocation = bundleFile.toString(); + InputStream inStream = null; + if (!isOSGiBundle) { + // We need to repackage the bundle + ByteArrayOutputStream out = new ByteArrayOutputStream(); + JarOutputStream jarOut = new JarOutputStream(out, manifest); + + String classpath = manifest.getMainAttributes().getValue("Bundle-ClassPath"); + boolean embedded = classpath != null && !classpath.trim().equals("."); + if (embedded) { + addFileToJar(bundleFile, jarOut); + } else { + copyJar(bundleFile, jarOut); + } + + jarOut.close(); + inStream = new ByteArrayInputStream(out.toByteArray()); + } else { + // The file itself is already a bundle + inStream = bundleFile.openStream(); + } + + try { + bundle = bundleContext.installBundle(bundleLocation, inStream); + if (logger.isLoggable(Level.FINE)) { + logger.fine("Bundle installed in " + (System.currentTimeMillis() - start) + " ms: " + bundleLocation); + } + tuscanyBundles.add(bundle); + return bundle; + } finally { + inStream.close(); + } + + } + + private Bundle findBundle(BundleContext bundleContext, String symbolicName, String version) { + Bundle[] bundles = bundleContext.getBundles(); + if (version == null) { + version = "0.0.0"; + } + for (Bundle b : bundles) { + String v = (String)b.getHeaders().get(BUNDLE_VERSION); + if (v == null) { + v = "0.0.0"; + } + if (b.getSymbolicName().equals(symbolicName) && (version.equals("0.0.0") || v.equals(version))) { + return b; + } + } + return null; + } + + private String getFileName(URL url) { + String name = url.getPath(); + int index = name.lastIndexOf('/'); + return name.substring(index + 1); + } + + private void addFileToJar(URL file, JarOutputStream jarOut) throws IOException { + JarEntry ze = new JarEntry(getFileName(file)); + jarOut.putNextEntry(ze); + InputStream inStream = file.openStream(); + copy(inStream, jarOut); + inStream.close(); + } + + private void copy(InputStream in, OutputStream out) throws IOException { + byte[] readBuf = new byte[4096]; + int bytesRead; + while ((bytesRead = in.read(readBuf)) > 0) { + out.write(readBuf, 0, bytesRead); + } + } + + private void copyJar(URL in, JarOutputStream jarOut) throws IOException { + JarInputStream jarIn = new JarInputStream(in.openStream()); + ZipEntry ze; + while ((ze = jarIn.getNextEntry()) != null) { + // Skip the MANIFEST.MF + if (ze.getName().equals("META-INF/MANIFEST.MF")) + continue; + jarOut.putNextEntry(ze); + copy(jarIn, jarOut); + } + jarIn.close(); + } + + private Manifest readManifest(URL jarFile) throws IOException { + JarInputStream jar = new JarInputStream(jarFile.openStream()); + // Read the Manifest from the jar file + Manifest manifest = jar.getManifest(); + jar.close(); + + if (manifest == null) { + // Create a new one if no Manifest is found + manifest = new Manifest(); + } + return manifest; + } + + private Manifest updateBundleManifest(URL jarFile, Manifest manifest) throws Exception { + + // Check if we have an associated .mf file + String name = jarFile.toString(); + int index = name.lastIndexOf('.'); + if (index != -1) { + URL mf = new URL(name.substring(0, index) + ".mf"); + try { + InputStream in = mf.openStream(); + manifest.read(in); + in.close(); + } catch (IOException e) { + // Ignore + } + } + + String jarFileName = getFileName(jarFile); + boolean isImmutableJar = false; + for (String immutableJar : immutableJars) { + if (jarFileName.startsWith(immutableJar)) { + isImmutableJar = true; + break; + } + } + + Attributes attributes = manifest.getMainAttributes(); + if (attributes.getValue(BUNDLE_SYMBOLICNAME) == null) { + String bundleSymbolicName = jarFileName; + if (bundleSymbolicName.endsWith(".jar")) { + bundleSymbolicName = bundleSymbolicName.substring(0, bundleSymbolicName.length() - 4); + } + attributes.putValue(BUNDLE_SYMBOLICNAME, bundleSymbolicName); + } else { + // Assume the jar is already a bundle + return null; + } + + if (attributes.getValue("Manifest-Version") == null) { + attributes.putValue("Manifest-Version", "1.0"); + } + + if (attributes.getValue(BUNDLE_MANIFESTVERSION) == null) { + attributes.putValue(BUNDLE_MANIFESTVERSION, "2"); + } + + if (isImmutableJar && attributes.getValue(BUNDLE_CLASSPATH) == null) { + attributes.putValue(BUNDLE_CLASSPATH, ".," + jarFileName); + } + + JarInputStream jar = new JarInputStream(jarFile.openStream()); + HashSet<String> packages = getPackagesInJar(jarFileName, jar); + jar.close(); + String version = getJarVersion(jarFileName); + + // attributes.remove(new Attributes.Name("Require-Bundle")); + // attributes.remove(new Attributes.Name("Import-Package")); + + if (attributes.getValue(BUNDLE_VERSION) == null) { + attributes.putValue(BUNDLE_VERSION, version); + } + // Existing export statements in bundles may contain versions, so they should be used as is + // SDO exports are not sufficient, and should be changed + if (attributes.getValue(EXPORT_PACKAGE) == null || jarFileName.startsWith("tuscany-sdo-impl")) { + String pkgs = packagesToString(packages, version); + if (pkgs.length() > 0) { + attributes.putValue(EXPORT_PACKAGE, pkgs); + attributes.putValue(IMPORT_PACKAGE, packagesToString(packages, null)); + } + // attributes.putValue("Import-Package", packagesToString(packages, null)); + } + + attributes.putValue(DYNAMICIMPORT_PACKAGE, "*"); + return manifest; + } + + private HashSet<String> getPackagesInJar(String bundleName, JarInputStream jar) throws Exception { + HashSet<String> packages = new HashSet<String>(); + ZipEntry entry; + while ((entry = jar.getNextEntry()) != null) { + String entryName = entry.getName(); + if (!entry.isDirectory() && entryName != null && entryName.length() > 0 && !entryName.startsWith(".") + // && !entryName.startsWith("META-INF") + && entryName.lastIndexOf("/") > 0) { + String pkg = entryName.substring(0, entryName.lastIndexOf("/")).replace('/', '.'); + packages.add(pkg); + + } + } + // FIXME: Split package + if (bundleName.startsWith("axis2-adb")) { + packages.remove("org.apache.axis2.util"); + } else if (bundleName.startsWith("axis2-codegen")) { + packages.remove("org.apache.axis2.wsdl"); + packages.remove("org.apache.axis2.wsdl.util"); + } else if (bundleName.startsWith("bsf-all")) { + packages.remove("org.mozilla.javascript"); + } + + return packages; + } + + private String packagesToString(HashSet<String> packages, String version) { + + StringBuilder pkgBuf = new StringBuilder(); + for (String pkg : packages) { + if (pkgBuf.length() > 0) { + pkgBuf.append(','); + } + pkgBuf.append(pkg); + if (version != null && !pkg.startsWith("META-INF.")) { + pkgBuf.append(";version=\""); + pkgBuf.append(version); + pkgBuf.append('\"'); + } + } + return pkgBuf.toString(); + } + + private String getJarVersion(String bundleName) { + Pattern pattern = Pattern.compile("-([0-9.]+)"); + Matcher matcher = pattern.matcher(bundleName); + String version = "1.0.0"; + if (matcher.find()) { + version = matcher.group(); + if (version.endsWith(".")) { + version = version.substring(1, version.length() - 1); + } else { + version = version.substring(1); + } + } + return version; + } + + public BundleContext getBundleContext() { + return bundleContext; + } + + public void bundleChanged(BundleEvent event) { + } + +} |