--- a/make/java/module/Makefile Fri Jul 18 12:47:04 2008 -0700
+++ b/make/java/module/Makefile Tue Jul 22 17:55:50 2008 -0700
@@ -36,6 +36,7 @@ AUTO_FILES_JAVA_DIRS = \
AUTO_FILES_JAVA_DIRS = \
java/module \
sun/module
+AUTO_JAVA_PRUNE = osgi
#
# Directories
--- a/make/sun/Makefile Fri Jul 18 12:47:04 2008 -0700
+++ b/make/sun/Makefile Tue Jul 22 17:55:50 2008 -0700
@@ -63,7 +63,7 @@ SUBDIRS = jar security javazic misc net
$(HEADLESS_SUBDIR) $(DGA_SUBDIR) \
font jpeg cmm applet rmi beans $(JDBC_SUBDIR) \
jawt text nio launcher management $(ORG_SUBDIR) \
- native2ascii serialver tools jconsole tracing
+ native2ascii serialver tools jconsole tracing module
all build clean clobber::
$(SUBDIRS-loop)
--- a/src/share/classes/sun/module/core/ExtensionModuleLoader.java Fri Jul 18 12:47:04 2008 -0700
+++ b/src/share/classes/sun/module/core/ExtensionModuleLoader.java Tue Jul 22 17:55:50 2008 -0700
@@ -35,6 +35,8 @@ import java.security.SecureClassLoader;
import java.security.SecureClassLoader;
import java.util.*;
+import sun.module.repository.RepositoryConfig;
+
/**
* This class represents the extension module loader that is used by the
* extension classloader for loading classes and resources from extension
@@ -71,7 +73,7 @@ public final class ExtensionModuleLoader
List<ModuleDefinition> extensionModuleDefs = new ArrayList<ModuleDefinition>();
final Repository bootstrapRepository = Repository.getBootstrapRepository();
- Repository extensionRepository = Repository.getSystemRepository();
+ Repository extensionRepository = RepositoryConfig.getSystemRepository(false);
if (extensionRepository != bootstrapRepository) {
@@ -88,7 +90,7 @@ public final class ExtensionModuleLoader
} else {
// Locating the system extension repository by looking up the immediate child
// of the extension repository, starting from the system repository.
- Repository systemExtensionRepository = Repository.getSystemRepository();
+ Repository systemExtensionRepository = RepositoryConfig.getSystemRepository(false);
while (systemExtensionRepository.getParent() != extensionRepository) {
systemExtensionRepository = systemExtensionRepository.getParent();
--- a/src/share/classes/sun/module/repository/RepositoryConfig.java Fri Jul 18 12:47:04 2008 -0700
+++ b/src/share/classes/sun/module/repository/RepositoryConfig.java Tue Jul 22 17:55:50 2008 -0700
@@ -39,6 +39,7 @@ import java.net.MalformedURLException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
+import java.net.URLClassLoader;
import java.net.URISyntaxException;
import java.security.AccessController;
import java.security.PrivilegedAction;
@@ -163,13 +164,37 @@ public final class RepositoryConfig
/**
* Returns the current system repository, or if one has not been set (for
* example, by the ModuleLauncher), creates it first.
+ * @param initializeAll initialize all repositories
* @return the system repository
*/
- public static synchronized Repository getSystemRepository() {
+ public static synchronized Repository getSystemRepository(boolean initializeAll) {
if (!configDone) {
systemRepository = configRepositories();
}
+ // XXX: Will revisit the bootstrapping issue
+ // During the VM startup, we can only initialize the repositories
+ // provided by rt.jar (loaded by bootstrap class loader).
+ // This method may be called when initializing the application class
+ // loader. Loading and initialize the OSGi and user-defined repositories
+ // (classes not loaded by bootstrap class loader) may result
+ // in a deadlock if loaded by the application class loader.
+ //
+ if (initializeAll) {
+ for (Repository r = systemRepository; r != null; r = r.getParent()) {
+ try {
+ if (!r.isActive()) {
+ r.initialize();
+ }
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+ }
return systemRepository;
+ }
+
+ public static synchronized Repository getSystemRepository() {
+ return getSystemRepository(true);
}
/**
@@ -468,6 +493,58 @@ public final class RepositoryConfig
}
/**
+ * Creates an OSGi repository
+ */
+ private static class OSGiRepositoryCreator extends RepositoryCreator {
+ static final OSGiRepositoryCreator instance = new OSGiRepositoryCreator();
+ private OSGiRepositoryCreator() { }
+
+ /** Name of Repository subclass to create. */
+ private String className = "sun.module.osgi.OSGiRepository";
+ private final String repoJar = "file:///${java.home}/lib/osgi-repo.jar";
+ private final String containerAttr = "container";
+
+ void setClassName(String name) {
+ className = name;
+ }
+ URLClassLoader getURLClassLoader(File container) throws IOException {
+ URL[] urls = new URL[2];
+ try {
+ urls[0] = new URL(PropertyExpander.expand(repoJar));
+ urls[1] = container.toURI().toURL();
+ } catch (PropertyExpander.ExpandException ex) {
+ // XXX: should not reach here
+ ex.printStackTrace();
+ }
+ return URLClassLoader.newInstance(urls);
+ }
+
+ protected Repository create(Repository parent, String repoName,
+ String sourceName, Map<String, String> config)
+ throws IOException, InstantiationException, NoSuchMethodException,
+ ClassNotFoundException, IllegalAccessException,
+ InvocationTargetException, URISyntaxException {
+ URI u = sourceName == null ? null : new URI(sourceName);
+ String containerPath = config.get(containerAttr);
+ File container;
+ try {
+ container = new File(new URI(containerPath));
+ } catch (URISyntaxException e) {
+ container = new File(containerPath);
+ }
+ if (!container.exists()) {
+ throw new IOException("OSGi container \"" + container +
+ "\" does not exist");
+ }
+
+ Class<?> clazz = Class.forName(className, true, getURLClassLoader(container));
+ Constructor ctor = clazz.getDeclaredConstructor(
+ String.class, URI.class, Repository.class, Map.class);
+ return (Repository) ctor.newInstance(repoName, u, parent, config);
+ }
+ }
+
+ /**
* Creates repositories based on the given {@code LinkedHashMap}, whose
* keys are presumed to be ordered such that the first entry describes the
* repository to create as a child of the bootstrap repository, the next
@@ -496,13 +573,17 @@ public final class RepositoryConfig
}
RepositoryCreator creator = DefaultCreator.instance;
- if (clazzName != null || factoryName != null) {
- if (clazzName != null) {
- ClassBasedCreator.instance.setClassName(clazzName);
- creator = ClassBasedCreator.instance;
- } else {
- FactoryBasedCreator.instance.setFactoryName(factoryName);
- creator = FactoryBasedCreator.instance;
+ if (repoName.equals("osgi")) {
+ creator = OSGiRepositoryCreator.instance;
+ } else {
+ if (clazzName != null || factoryName != null) {
+ if (clazzName != null) {
+ ClassBasedCreator.instance.setClassName(clazzName);
+ creator = ClassBasedCreator.instance;
+ } else {
+ FactoryBasedCreator.instance.setFactoryName(factoryName);
+ creator = FactoryBasedCreator.instance;
+ }
}
}
@@ -532,7 +613,7 @@ public final class RepositoryConfig
} catch (Exception ex) {
throw new IllegalArgumentException(
"Cannot create repository named '" + repoName + "' at location '" + sourceName
- + "': " + ex);
+ + "': " + ex, ex);
}
/*
--- a/src/share/lib/module/repository.properties Fri Jul 18 12:47:04 2008 -0700
+++ b/src/share/lib/module/repository.properties Tue Jul 22 17:55:50 2008 -0700
@@ -65,3 +65,19 @@ user.parent=global
user.parent=global
user.factoryname=sun.module.repository.UserRepositoryFactory
user.source=file:///${user.home}/.java/module
+
+#
+# The OSGi repository contains OSGi bundles. To set up the OSGi
+# repository, you need to install the Felix OSGi container
+# and uncomment the osgi properties below and:
+# 1. set "osgi.container" to the pathname of the Felix OSGi
+# container (felix.jar)
+# 2. set "osgi.source" to the source location for the OSGi bundles
+#
+# Known bug: The VM does not exit when the application terminates
+# when Felix 1.0.4 is used because the FelixDispatchQueue thread
+# started by Felix runtime is a non-daemon thread.
+#
+#osgi.parent=user
+#osgi.container=file:///${java.home}/lib/felix-1.0.4/bin/felix.jar
+#osgi.source=file:///${java.home}/lib/felix-1.0.4/bundle
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/make/sun/module/Makefile Tue Jul 22 17:55:50 2008 -0700
@@ -0,0 +1,34 @@
+#
+# Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation. Sun designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Sun in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+# CA 95054 USA or visit www.sun.com if you need additional information or
+# have any questions.
+#
+
+BUILDDIR = ../..
+PRODUCT = sun
+include $(BUILDDIR)/common/Defs.gmk
+
+SUBDIRS = osgi
+
+all build clean clobber::
+ $(SUBDIRS-loop)
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/make/sun/module/osgi/Makefile Tue Jul 22 17:55:50 2008 -0700
@@ -0,0 +1,87 @@
+#
+# Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation. Sun designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Sun in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+# CA 95054 USA or visit www.sun.com if you need additional information or
+# have any questions.
+#
+
+#
+# Makefile for building OSGi repository
+#
+
+BUILDDIR = ../../..
+PACKAGE = sun.module.osgi
+PRODUCT = sun
+include $(BUILDDIR)/common/Defs.gmk
+
+ifndef OPENJDK
+ # OSGi repository depends on the OSGi framework API
+ #
+ # Temporary workaround: osgi-repo.jar will be built only
+ # if OSGI_FRAMEWORK_JAR and FELIX_JAR are set
+ MODULES_LIB = $(CLOSED_SHARE_SRC)/lib/module
+ ifndef OSGI_FRAMEWORK_JAR
+ OSGI_FRAMEWORK_JAR = $(MODULES_LIB)/org.osgi.core-1.0.1.jar
+ endif
+ ifndef FELIX_JAR
+ FELIX_JAR = $(MODULES_LIB)/felix.jar
+ endif
+endif
+
+OSGI_REPO_DEST = $(LIBDIR)/osgi-repo.jar
+
+EXISTS := $(call FileExists,$(OSGI_FRAMEWORK_JAR),,)
+ifneq ("$(EXISTS)", "NO_FILE_EXISTS")
+ TARGET = classes $(OSGI_REPO_DEST)
+endif
+
+ifdef OSGI_FRAMEWORK_JAR
+ #
+ # Files to compile
+ #
+ AUTO_FILES_JAVA_DIRS = sun/module/osgi
+ #
+ # Temporary workaround to include the OSGi framework
+ # and Felix OSGi container for compilation.
+ OTHER_JAVACFLAGS = -cp $(OSGI_FRAMEWORK_JAR):$(FELIX_JAR)
+endif
+
+#
+# Rules.
+#
+include $(BUILDDIR)/common/Classes.gmk
+
+all: $(TARGET)
+
+#
+# Extra rule to build osgi-repo.jar
+#
+
+$(OSGI_REPO_DEST): $(LIBDIR) $(FILES_class)
+ $(BOOT_JAR_CMD) -cf $(OSGI_REPO_DEST) \
+ -C $(CLASSBINDIR) sun/module/osgi \
+ $(JAR_JFLAGS)
+ $(RM) -rf $(CLASSBINDIR)/sun/module/osgi
+ @$(java-vm-cleanup)
+
+clean clobber::
+ $(RM) $(OSGI_REPO_DEST)
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/module/osgi/BundleClassLoader.java Tue Jul 22 17:55:50 2008 -0700
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.module.osgi;
+
+import java.module.Module;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Vector;
+import java.net.URL;
+import java.security.SecureClassLoader;
+import org.osgi.framework.Bundle;
+
+
+/**
+ * Class loader implementation for the module system.
+ */
+final class BundleClassLoader extends SecureClassLoader {
+
+ private final OSGiModule module;
+ private final OSGiModuleDefinition moduleDef;
+ private final Bundle bundle;
+
+ BundleClassLoader(OSGiModule osgiModule, OSGiModuleDefinition moduleDef) {
+ super(null);
+ this.module = osgiModule;
+ this.moduleDef = moduleDef;
+ this.bundle = moduleDef.getBundle();
+ }
+
+ @Override
+ public Module getModule() {
+ return module;
+ }
+
+ @Override
+ protected synchronized Class<?> loadClass(String name, boolean resolve)
+ throws ClassNotFoundException {
+ // First, check if the class has already been loaded
+ Class<?> c = findLoadedClass(name);
+
+ if (c == null) {
+ // check myself
+ c = bundle.loadClass(name);
+ }
+
+ // we do not delegate to the parent
+
+ if (resolve) {
+ resolveClass(c);
+ }
+ return c;
+ }
+
+ @Override
+ public InputStream getResourceAsStream(String name) {
+ if (name == null) {
+ throw new NullPointerException();
+ }
+ InputStream is = null;
+ try {
+ URL url = getResource(name);
+ if (url != null) {
+ is = url.openStream();
+ }
+ } catch (IOException e) {
+ }
+ return is;
+ }
+
+ @Override
+ public URL getResource(String name) {
+ if (name == null) {
+ throw new NullPointerException();
+ }
+ return findResource(name);
+ }
+
+ @Override
+ public Enumeration<URL> getResources(String name) throws IOException {
+ if (name == null) {
+ throw new NullPointerException();
+ }
+ Vector<URL> v = new Vector<URL>();
+ for (URL url : Collections.list(findResources(name))) {
+ if (url != null) {
+ v.add(url);
+ }
+ }
+ return v.elements();
+ }
+
+ @Override
+ protected URL findResource(String name) {
+ return bundle.getResource(name);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected Enumeration<URL> findResources(String name) throws IOException {
+ return bundle.getResources(name);
+ }
+
+ @Override
+ protected String findLibrary(String libname) {
+ if (libname == null) {
+ throw new NullPointerException();
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+
+ builder.append("BundleLoaderClass[bundle=");
+ builder.append(moduleDef.getName());
+ builder.append(", v");
+ builder.append(moduleDef.getVersion());
+ builder.append("]");
+
+ return builder.toString();
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/module/osgi/BundleManifestMapper.java Tue Jul 22 17:55:50 2008 -0700
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.module.osgi;
+
+import java.module.Modules;
+import java.module.ModuleDefinition;
+import java.module.ImportDependency;
+import java.module.PackageDefinition;
+import java.module.Version;
+import java.module.VersionConstraint;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.osgi.framework.Bundle;
+import static org.osgi.framework.Constants.*;
+
+/**
+ * This class maps from the bundle manifest to the ModuleDefinition.
+ */
+class BundleManifestMapper {
+
+ private final ModuleDefinition moduleDef;
+ private final Bundle bundle;
+ private final Map<String, String> headers;
+ private final String symbolicName;
+ private static final String DEFAULT_OSGI_VERSION =
+ org.osgi.framework.Version.emptyVersion.toString();
+
+ /**
+ * TODO:
+ * <ol>
+ * <li>getHeader(Locale)</li>
+ * </ol>
+ */
+ BundleManifestMapper(ModuleDefinition modDef, Bundle bundle) {
+ this.moduleDef = modDef;
+ this.bundle = bundle;
+ this.headers = new HashMap<String, String>();
+ Dictionary dict = bundle.getHeaders();
+ for (Enumeration keys = dict.keys(); keys.hasMoreElements();) {
+ Object k = keys.nextElement();
+ headers.put((String) k, (String) dict.get(k));
+ }
+
+ String header = headers.get(BUNDLE_SYMBOLICNAME);
+ // XXX: for now - NullPointerException if header == null
+ // should check the bundle version
+ String[] entries = header.split(";");
+ this.symbolicName = entries[0];
+ }
+
+ /**
+ * Returns the Version object of the bundle.
+ */
+ static Version getVersion(Bundle bundle) {
+ String osgiVersion = (String) bundle.getHeaders().get(BUNDLE_VERSION);
+ return convertOSGiVersion(osgiVersion);
+ }
+
+ private static Version convertOSGiVersion(String osgiVersion) {
+ // Comparing two versions with and without the qualifier
+ // OSGi version 1.2.3 > 1.2.3.qualifier
+ // Java module version 1.2.3 < 1.2.3-qualifier
+ String version = osgiVersion.trim();
+ org.osgi.framework.Version v =
+ org.osgi.framework.Version.parseVersion(version);
+ if (v.getQualifier().isEmpty()) {
+ return Version.valueOf(version);
+ } else {
+ return Version.valueOf(v.getMajor(), v.getMinor(), v.getMicro(), 1, v.getQualifier());
+ }
+ }
+
+ private static VersionConstraint convertOSGiVersionRange(String osgiVersionRange) {
+ if (osgiVersionRange.startsWith("[") || osgiVersionRange.startsWith("(")) {
+ // parse the version range
+ int len = osgiVersionRange.length();
+ String v[] = osgiVersionRange.substring(1, len-1).split(",");
+
+ Version v0 = convertOSGiVersion(v[0]);
+ Version v1 = convertOSGiVersion(v[1]);
+ String versionRange = osgiVersionRange.substring(0,1) +
+ v0 + "," + v1 + osgiVersionRange.substring(len-1, len);
+
+ return VersionConstraint.valueOf(versionRange);
+ } else {
+ // single version - append "+" before conversion
+ Version v = convertOSGiVersion(osgiVersionRange);
+ return VersionConstraint.valueOf(osgiVersionRange.trim() + "+");
+ }
+ }
+
+ String getSymbolicName() {
+ // XXX: Felix implementation of bundle.getSymbolicName()
+ // returns the entire Bundle-SymbolicName manifest header
+ return symbolicName;
+ }
+
+ Version getVersion() {
+ return convertOSGiVersion(headers.get(BUNDLE_VERSION));
+ }
+
+ List<ImportDependency> getImportDependencies() {
+ List<ImportDependency> dependencies = new ArrayList<ImportDependency>();
+
+ // parse Require-Bundle header
+ String header = headers.get(REQUIRE_BUNDLE);
+ if (header != null) {
+ String[] requireBundles = header.split(",");
+ for (String reqBundle : requireBundles) {
+ String[] entries = reqBundle.split(";");
+ String importName = entries[0].trim();
+ String versionRange =
+ getAttribute(entries, BUNDLE_VERSION_ATTRIBUTE, DEFAULT_OSGI_VERSION);
+ VersionConstraint versionConstraint = convertOSGiVersionRange(versionRange);
+ String visibility =
+ getDirective(entries, VISIBILITY_DIRECTIVE, VISIBILITY_PRIVATE);
+ String resolution =
+ getDirective(entries, RESOLUTION_DIRECTIVE, RESOLUTION_MANDATORY);
+
+ dependencies.add(Modules.newModuleDependency(
+ importName,
+ versionConstraint,
+ visibility.equals(VISIBILITY_REEXPORT),
+ resolution.equals(RESOLUTION_OPTIONAL),
+ buildAttributeMap(entries)));
+
+ }
+ }
+
+ // parse Import-Package header
+
+ header = headers.get(IMPORT_PACKAGE);
+ if (header != null) {
+ String[] importPkgs = header.split(",");
+
+ for (String importPackage : importPkgs) {
+ String[] entries = importPackage.split(";");
+ String importName = entries[0].trim();
+ String versionRange =
+ getAttribute(entries, VERSION_ATTRIBUTE, DEFAULT_OSGI_VERSION);
+ VersionConstraint versionConstraint = convertOSGiVersionRange(versionRange);
+ String resolution =
+ getDirective(entries, RESOLUTION_DIRECTIVE, RESOLUTION_MANDATORY);
+
+ dependencies.add(Modules.newPackageDependency(
+ importName,
+ versionConstraint,
+ false,
+ resolution.equals(RESOLUTION_OPTIONAL),
+ buildAttributeMap(entries)));
+
+ }
+ }
+ return dependencies;
+ }
+
+ Set<PackageDefinition> getExportedPackageDefinitions() {
+ Set<PackageDefinition> exports = new HashSet<PackageDefinition>();
+
+ // parse Export-Package header
+ String header = headers.get(EXPORT_PACKAGE);
+ if (header != null) {
+ String[] exportedPkgs = header.split(",");
+ for (String exportPackage : exportedPkgs) {
+ String[] entries = exportPackage.split(";");
+ String exportName = entries[0].trim();
+ String version =
+ getAttribute(entries, VERSION_ATTRIBUTE, DEFAULT_OSGI_VERSION);
+ PackageDefinition pkgDef =
+ new OSGiPackageDefinition(exportName,
+ convertOSGiVersion(version),
+ moduleDef,
+ buildAttributeMap(entries));
+ exports.add(pkgDef);
+ }
+ }
+ return exports;
+ }
+
+ Set<PackageDefinition> getMemberPackageDefinitions() {
+ Set<PackageDefinition> members = new HashSet<PackageDefinition>(getExportedPackageDefinitions());
+ // TODO: add other member packages
+ return members;
+ }
+
+ boolean isSingleton() {
+ String header = headers.get(BUNDLE_SYMBOLICNAME);
+ String value = "false"; // default
+ if (header != null) {
+ String[] entries = header.split(";");
+ value = getDirective(entries, SINGLETON_DIRECTIVE, "false");
+ }
+ return Boolean.valueOf(value);
+ }
+
+ private String getDirective(String[] entries, String directive, String defaultValue) {
+ for (String s : entries) {
+ if (s.contains(":=")) {
+ String[] ss = strtok(s, ":=");
+ if (ss[0].equals(directive)) {
+ return ss[1];
+ }
+ }
+ }
+ return defaultValue;
+ }
+
+ private String getAttribute(String[] entries, String attribute, String defaultValue) {
+ for (String s : entries) {
+ if (s.contains("=")) {
+ String[] ss = strtok(s, "=");
+ if (ss[0].equals(attribute)) {
+ return ss[1];
+ }
+ }
+ }
+ return defaultValue;
+ }
+
+ /**
+ * Returns the key and value in an array of String separated by
+ * the given delimiter.
+ *
+ */
+ private String[] strtok(String str, String delimiter) {
+ String[] result = new String[2];
+
+ String[] ss = str.trim().split(delimiter);
+ assert ss.length == 2;
+ result[0] = ss[0].trim();
+ String value = ss[1].trim();
+ if (value.startsWith("\"")) {
+ result[1] = value.substring(1, value.length() - 1);
+ } else {
+ result[1] = value;
+ }
+ return result;
+ }
+
+ private Map<String, String> buildAttributeMap(String[] entries) {
+ Map<String, String> attributes = new HashMap<String, String>();
+ for (String s : entries) {
+ String[] ss;
+ if (s.contains(":=")) {
+ ss = strtok(s, ":=");
+ } else if (s.contains("=")) {
+ ss = strtok(s, "=");
+ } else {
+ // Not a directive nor an attribute
+ continue;
+ }
+ attributes.put(ss[0], ss[1]);
+ }
+ return attributes;
+ }
+
+ Map<String, String> getModuleAttributesMap() {
+ // TODO: should we return all manifest headers?
+ return Collections.unmodifiableMap(headers);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/module/osgi/OSGiModule.java Tue Jul 22 17:55:50 2008 -0700
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.module.osgi;
+
+import java.module.Module;
+import java.module.ModuleDefinition;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Module for an OSGi bundle.
+ */
+class OSGiModule extends Module {
+
+ private OSGiModuleDefinition moduleDef;
+ private BundleClassLoader classLoader;
+
+ // List of imported module providers that are added
+ // during the resolving process.
+ private List<Module> importedModules;
+
+ // Set of modules that import the current module.
+ private Set<Module> importingModules;
+
+ OSGiModule(OSGiModuleDefinition osgiModuleDef) {
+ super();
+ this.moduleDef = osgiModuleDef;
+ this.classLoader = new BundleClassLoader(this, osgiModuleDef);
+ this.importedModules = new ArrayList<Module>();
+ }
+
+ @Override
+ public ModuleDefinition getModuleDefinition() {
+ return moduleDef;
+ }
+
+ @Override
+ public ClassLoader getClassLoader() {
+ return classLoader;
+ }
+
+ /**
+ * This method is intended to be called by the OSGi module system
+ * to set up the imported dependency in OSGi modules.
+ */
+ void addImportedModule(Module m) {
+ importedModules.add(m);
+ }
+
+ @Override
+ public List<Module> getImportedModules() {
+ return Collections.unmodifiableList(importedModules);
+ }
+
+ /**
+ * Returns an unmodifiable list of module instances that imports this module
+ * instance.
+ */
+ Set<Module> getImportingModules() {
+ if (importingModules == null) {
+ throw new NullPointerException("Importing modules list has not been created yet");
+ }
+ return Collections.unmodifiableSet(importingModules);
+ }
+
+ /**
+ * Adds a module instance that imports this module instance.
+ */
+ void addImportingModule(Module module) {
+ if (importingModules == null) {
+ throw new NullPointerException("Importing modules list has not been created yet");
+ }
+ importingModules.add(module);
+ }
+
+ /**
+ * Removes a module instance that imports this module instance.
+ */
+ void removeImportingModule(Module module) {
+ // This may be called when a module instance gets into ERROR state,
+ // before the importing module list is created.
+ if (importingModules != null) {
+ importingModules.remove(module);
+ }
+ }
+
+ /**
+ * Removes all module instances that import this module instance.
+ */
+ void removeImportingModules() {
+ if (importingModules != null) {
+ importingModules.clear();
+ }
+ }
+
+ @Override
+ public boolean supportsDeepValidation() {
+ return false;
+ }
+
+ @Override
+ public void deepValidate() {
+ throw new UnsupportedOperationException(
+ "OSGi module cannot be deep validated.");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/module/osgi/OSGiModuleDefinition.java Tue Jul 22 17:55:50 2008 -0700
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.module.osgi;
+
+import java.lang.annotation.Annotation;
+import java.module.ImportDependency;
+import java.module.ModuleContent;
+import java.module.ModuleDefinition;
+import java.module.ModuleSystem;
+import java.module.ModuleSystemPermission;
+import java.module.PackageDefinition;
+import java.module.Repository;
+import java.module.Version;
+import java.util.Collections;
+import java.util.Set;
+import java.util.List;
+import org.osgi.framework.Bundle;
+import org.osgi.service.packageadmin.ExportedPackage;
+
+/**
+ * A ModuleDefinition for OSGi bundle.
+ */
+public class OSGiModuleDefinition extends ModuleDefinition {
+ private final Repository repository;
+ private final Bundle bundle;
+ private final String name;
+ private final Version version;
+ private final BundleManifestMapper mapper;
+ private final boolean isReleasable;
+
+ OSGiModuleDefinition(Repository repo, Bundle bundle) {
+ this.repository = repo;
+ this.bundle = bundle;
+ this.mapper = new BundleManifestMapper(this, bundle);
+ this.name = mapper.getSymbolicName();
+ this.version = mapper.getVersion();
+ // XXX: singleton bundle - should it be releasable?
+ this.isReleasable = (mapper.isSingleton() == false);
+ }
+
+ public Bundle getBundle() {
+ return bundle;
+ }
+
+ public Set<ExportedPackage> getExportedPackages() {
+ return OSGiRuntime.getExportedPackages(bundle);
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public Version getVersion() {
+ return version;
+ }
+
+ @Override
+ public Set<String> getAttributeNames() {
+ return mapper.getModuleAttributesMap().keySet();
+ }
+
+ @Override
+ public String getAttribute(String name) {
+ return mapper.getModuleAttributesMap().get(name);
+ }
+
+ @Override
+ public List<ImportDependency> getImportDependencies() {
+ return mapper.getImportDependencies();
+ }
+
+ @Override
+ public Repository getRepository() {
+ return repository;
+ }
+
+ @Override
+ public ModuleSystem getModuleSystem() {
+ return OSGiModuleSystem.getModuleSystem();
+ }
+
+ @Override
+ public String getMainClass() {
+ return null;
+ }
+
+ @Override
+ public Set<String> getMemberClasses() {
+ throw new UnsupportedOperationException("OSGiModuleDefinition.getMemberClasses not implemented");
+ }
+
+ @Override
+ public Set<PackageDefinition> getMemberPackageDefinitions() {
+ return mapper.getMemberPackageDefinitions();
+ }
+
+ @Override
+ public Set<String> getExportedClasses() {
+ throw new UnsupportedOperationException("OSGiModuleDefinition.getExportedClasses not implemented");
+ }
+
+ @Override
+ public Set<PackageDefinition> getExportedPackageDefinitions() {
+ return mapper.getExportedPackageDefinitions();
+ }
+
+ @Override
+ public boolean isClassExported(String name) {
+ // TODO: need to deal with include and exclude directive
+ String packageName = name.substring(0, name.lastIndexOf("."));
+ try {
+ Set<PackageDefinition> exports = getExportedPackageDefinitions();
+ for (PackageDefinition pkg : exports) {
+ if (pkg.getName().equals(packageName)) {
+ // FIXME: assume it's exported for now
+ return true;
+ }
+ }
+ return false;
+ } catch (UnsupportedOperationException uoe) {
+ return true;
+ }
+ }
+
+ @Override
+ public Set<String> getExportedResources() {
+ // TODO: to be implemented
+ throw new UnsupportedOperationException("OSGiModuleDefinition.getExportedResources not implemented");
+ }
+
+ @Override
+ public boolean isResourceExported(String path) {
+ // TODO: need to deal with include and exclude directive
+ try {
+ Set<String> exportedResources = getExportedResources();
+ return exportedResources.contains(path);
+ } catch (UnsupportedOperationException uoe) {
+ return true;
+ }
+ }
+
+ @Override
+ public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
+ if (annotationClass == null) {
+ throw new NullPointerException();
+ }
+ // No annotation
+ return null;
+ }
+
+ @Override
+ public List<Annotation> getAnnotations() {
+ // No annotation
+ return Collections.emptyList();
+ }
+
+ @Override
+ public boolean isModuleReleasable() {
+ return isReleasable;
+ }
+
+ @Override
+ public ModuleContent getModuleContent() {
+ throw new UnsupportedOperationException("ModuleContent is not supported for OSGi bundle");
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/module/osgi/OSGiModuleSystem.java Tue Jul 22 17:55:50 2008 -0700
@@ -0,0 +1,336 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.module.osgi;
+
+import java.module.ModuleSystem;
+import java.module.ModuleDefinition;
+import java.module.ModuleInitializationException;
+
+import java.module.ImportDependency;
+import java.module.ModuleDependency;
+import java.module.PackageDependency;
+import java.module.Module;
+import java.module.ModuleDefinition;
+import java.module.ModuleSystem;
+import java.module.ModuleSystemPermission;
+import java.module.ModuleSystemEvent;
+import java.module.Repository;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.Map;
+import java.util.Set;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+
+import static java.module.ModuleSystemEvent.Type.*;
+
+/**
+ * This class is just a wrapper. The OSGi container resolves the
+ * bundles and all the work.
+ */
+class OSGiModuleSystem extends ModuleSystem {
+ private final static OSGiModuleSystem osgiModuleSystem =
+ new OSGiModuleSystem();
+
+ private final Map<ModuleDefinition, OSGiModule> modules =
+ new HashMap<ModuleDefinition, OSGiModule>();
+
+ // Module definitions that have been disabled.
+ private final Set<Long> disabledModuleDefs = new HashSet<Long>();
+
+ private OSGiModuleSystem() {
+ }
+
+ static OSGiModuleSystem getModuleSystem() {
+ return osgiModuleSystem;
+ }
+
+ @Override
+ public synchronized void releaseModule(ModuleDefinition moduleDef) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new ModuleSystemPermission("releaseModule"));
+ }
+ if (moduleDef.getModuleSystem() != this) {
+ throw new IllegalArgumentException("Module definition is associated with another module system.");
+ }
+ if (moduleDef.isModuleReleasable() == false) {
+ throw new UnsupportedOperationException("Module instance is not releasable.");
+ }
+ OSGiModule moduleToRelease = modules.get(moduleDef);
+ if (moduleToRelease == null) {
+ // There is no module instance that is fully initialized, partially
+ // initialized, or in error state, which corresponds to the module
+ // defintion. Therefore, release module instance is a no-op.
+ return;
+ }
+
+ // Otherwise, module instance is either fully initialized, partially
+ // initialized, or in error state. Call getModule() to either a
+ // fully initialized module instance, or obtain an initialization
+ // exception.
+ try {
+ getModuleInternal(moduleDef);
+ } catch(ModuleInitializationException mie) {
+ // No module instance is instantiated successfully, thus nothing
+ // to release.
+ return;
+ }
+
+ // Determine the transitive closure of the importing modules using
+ // BFS (Breadth-First-Search)
+ Set<Module> importingModulesClosure =
+ findImportingModulesClosure(moduleToRelease);
+
+ // First, releases all importing module instances that are from
+ // this module system.
+ for (Module m : importingModulesClosure) {
+ ModuleDefinition md = m.getModuleDefinition();
+ if (md.getModuleSystem() != this) {
+ continue;
+ }
+ if (md.isModuleReleasable() == false) {
+ continue;
+ }
+
+ OSGiModule mi = modules.get(md);
+
+ // Visited modules may have been released previously through releaseModule()
+ if (mi == null) {
+ continue;
+ }
+
+ // Releases cached module instance to avoid potential recursion.
+ modules.remove(md);
+
+ // Removes references to all importing modules
+ mi.removeImportingModules();
+
+ // TODO: stop the bundle to release module
+
+ // Send MODULE_RELEASED event
+ ModuleSystemEvent evt = new ModuleSystemEvent(this, MODULE_RELEASED, moduleToRelease, moduleDef, null);
+ processEvent(evt);
+ }
+
+ // Then, releases all all importing module instances that are from
+ // other module systems.
+
+ for (Module m : importingModulesClosure) {
+ ModuleDefinition md = m.getModuleDefinition();
+ ModuleSystem ms = md.getModuleSystem();
+ if (ms == this) {
+ continue;
+ }
+ if (md.isModuleReleasable()) {
+ ms.releaseModule(md);
+ }
+ }
+ }
+
+ @Override
+ public void disableModuleDefinition(ModuleDefinition moduleDef) {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new ModuleSystemPermission("disableModuleDefinition"));
+ }
+ if (moduleDef.getModuleSystem() != this) {
+ throw new UnsupportedOperationException("Cannot disable module definition in a different module system..");
+ }
+
+ long id = getOSGiModuleDefinition(moduleDef).getBundle().getBundleId();
+
+ synchronized(disabledModuleDefs) {
+ if (disabledModuleDefs.contains(id)) {
+ throw new IllegalStateException("Cannot disable module definition which has already been disabled.");
+ }
+ disabledModuleDefs.add(id);
+ }
+
+ // Send MODULE_DEFINITION_DISABLED event
+ ModuleSystemEvent evt = new ModuleSystemEvent(this, MODULE_DEFINITION_DISABLED, null, moduleDef, null);
+ processEvent(evt);
+ }
+
+ @Override
+ public Module getModule(ModuleDefinition moduleDef) throws ModuleInitializationException {
+ long id = getOSGiModuleDefinition(moduleDef).getBundle().getBundleId();
+
+ // Check if the module definition has been disabled.
+ //
+ synchronized(disabledModuleDefs) {
+ if (disabledModuleDefs.contains(id)) {
+ throw new IllegalStateException("Cannot instantiate new module instance from a disabled module definition.");
+ }
+ }
+ return getModuleInternal(moduleDef);
+ }
+
+ // @Override
+ public List<Module> getModules(ModuleDefinition importer, List<ModuleDefinition> moduleDefs)
+ throws ModuleInitializationException {
+ for (ModuleDefinition moduleDef : moduleDefs) {
+ long id = getOSGiModuleDefinition(moduleDef).getBundle().getBundleId();
+ // Check if the module definition has been disabled.
+ synchronized(disabledModuleDefs) {
+ if (disabledModuleDefs.contains(id)) {
+ throw new IllegalStateException("Cannot instantiate new module instance from a disabled module definition.");
+ }
+ }
+ }
+
+ // XXX: To investigate how this impacts the wiring
+ List<Module> result = new ArrayList<Module>();
+ for (ModuleDefinition moduleDef : moduleDefs) {
+ result.add(getModuleInternal(moduleDef));
+ }
+ return result;
+ }
+
+ private OSGiModule getModuleInternal(ModuleDefinition moduleDef) throws ModuleInitializationException {
+ // Checks if the module system has instantiated a module instance for
+ // the specified module definition before.
+ OSGiModule m = modules.get(moduleDef);
+ if (m != null) {
+ return m;
+ }
+
+ OSGiModuleDefinition osgiModuleDef = getOSGiModuleDefinition(moduleDef);
+ Bundle bundle = osgiModuleDef.getBundle();
+
+ if (bundle.getState() == Bundle.UNINSTALLED) {
+ throw new ModuleInitializationException("Cannot instantiate a module definition that is uninstalled.");
+ }
+
+ try {
+ // Before an OSGi bundle could be used, we must start it if it hasn't.
+ bundle.start();
+ } catch (BundleException bex) {
+ throw new ModuleInitializationException("Cannot start OSGi bundle", bex);
+ }
+
+ m = new OSGiModule(osgiModuleDef);
+
+ // Put the module instance into the map first, so the same module
+ // instance could be found in case this method is called recursively.
+ modules.put(moduleDef, m);
+
+ // Based on the import dependency, set up the appropriate imported
+ // modules.
+ for (ImportDependency imp : moduleDef.getImportDependencies()) {
+ Bundle importedBundle = null;
+ if (imp instanceof PackageDependency) {
+ importedBundle = OSGiRuntime.getExportingBundle(bundle, imp.getName());
+ } else if (imp instanceof ModuleDependency) {
+ importedBundle = OSGiRuntime.getRequiredBundle(bundle, imp.getName());
+ } else {
+ throw new ModuleInitializationException("Invalid ImportDependency type: " + imp);
+ }
+
+ if (importedBundle == null && imp.isOptional() == false) {
+ throw new ModuleInitializationException("Cannot resolve: " + imp);
+ }
+ // TODO: Assume there is only one OSGi repository
+ // and all OSGi bundles are exposed in that repository.
+ if (importedBundle != null) {
+ OSGiModuleDefinition importedMD =
+ new OSGiModuleDefinition(moduleDef.getRepository(),
+ importedBundle);
+
+ ModuleSystem importedModuleSystem = importedMD.getModuleSystem();
+ if (importedModuleSystem == this) {
+ // Get the raw module instance from the module system.
+ // If it has not been initialized yet, it is automatically
+ // enqueued.
+ Module importedModule = getModuleInternal(importedMD);
+ m.addImportedModule(importedModule);
+ } else {
+ // imported modules from the other module systems
+ // TODO: to be implemented
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+ }
+ }
+
+ // Send MODULE_INITIALIZED event
+ ModuleSystemEvent evt = new ModuleSystemEvent(this, MODULE_INITIALIZED, m, moduleDef, null);
+ this.processEvent(evt);
+
+ return m;
+ }
+
+ private OSGiModuleDefinition getOSGiModuleDefinition(ModuleDefinition moduleDef) {
+ if (moduleDef.getModuleSystem() != this) {
+ throw new UnsupportedOperationException("ModuleDefinition instantiated from module definitions in a different module system.");
+ }
+ return OSGiModuleDefinition.class.cast(moduleDef);
+ }
+
+ @Override
+ public String toString() {
+ return "OSGi module system";
+ }
+
+ /**
+ * Returns the transitive closure of the importing modules of a specified
+ * module.
+ *
+ * @param module module instance
+ * @return a set of modules in the importing transitive closure
+ */
+ private static Set<Module> findImportingModulesClosure(Module module) {
+ // Determine the transitive closure of the importing modules using
+ // BFS (Breadth-First-Search)
+ Set<Module> visitedModules = new HashSet<Module>();
+ Queue<Module> visitingQueue = new LinkedList<Module>();
+ visitingQueue.add(module);
+ while (visitingQueue.size() != 0) {
+ Module v = visitingQueue.remove();
+ if (v instanceof OSGiModule) {
+ // Module instance is from this module system; walk up the
+ // importer dependency graph.
+ for (Module importingModule : ((OSGiModule) v).getImportingModules()) {
+ // Check if we have visited this module instance before.
+ if (visitedModules.contains(importingModule)) {
+ continue;
+ }
+ visitingQueue.add(importingModule);
+ }
+ }
+ else {
+ // Module instance is from other module system; treat it as
+ // a leaf node.
+ }
+ visitedModules.add(v);
+ }
+ return visitedModules;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/module/osgi/OSGiPackageDefinition.java Tue Jul 22 17:55:50 2008 -0700
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.module.osgi;
+
+import java.lang.annotation.Annotation;
+import java.module.ModuleDefinition;
+import java.module.PackageDefinition;
+import java.module.Version;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * XXX: Remove this class when the module system framework
+ * provides a concrete implementation for PackageDefinition
+ */
+public class OSGiPackageDefinition extends PackageDefinition {
+ private String packageName;
+ private Version version;
+ private ModuleDefinition moduleDef;
+ private Map<String, String> attributes;
+
+ OSGiPackageDefinition(String packageName, Version version,
+ ModuleDefinition moduleDef, Map<String, String> attributes) {
+ this.packageName = packageName;
+ this.version = version;
+ this.moduleDef = moduleDef;
+ this.attributes = attributes;
+ }
+
+ @Override
+ public String getName() {
+ return packageName;
+ }
+
+ @Override
+ public Version getVersion() {
+ return version;
+ }
+
+ @Override
+ public ModuleDefinition getModuleDefinition() {
+ return moduleDef;
+ }
+
+ @Override
+ public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
+ if (annotationClass == null)
+ throw new NullPointerException();
+ // No annotation supported
+ return null;
+ }
+
+ @Override
+ public List<Annotation> getAnnotations() {
+ // No annotation supported
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Set<String> getAttributeNames() {
+ return Collections.unmodifiableSet(attributes.keySet());
+ }
+
+ @Override
+ public String getAttribute(String name) {
+ if (name == null) {
+ throw new NullPointerException("Attribute name must not be null.");
+ }
+ return attributes.get(name);
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/module/osgi/OSGiRepository.java Tue Jul 22 17:55:50 2008 -0700
@@ -0,0 +1,424 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.module.osgi;
+
+import java.io.File;
+import java.io.IOException;
+import java.module.ModuleArchiveInfo;
+import java.module.ModuleDefinition;
+import java.module.ModuleFormatException;
+import java.module.ModuleSystemPermission;
+import java.module.Repository;
+import java.net.URI;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+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.Set;
+
+import sun.module.repository.JamModuleArchiveInfo;
+import org.osgi.framework.Bundle;
+
+/**
+ * A repository for OSGi bundles.
+ *
+ * This repository will auto-install all bundles in the given source location
+ * when initialized.
+ */
+public class OSGiRepository extends Repository {
+ private final Map<String, String> config;
+ private Map<String, Map<ModuleArchiveInfo, OSGiModuleDefinition>> contentMapping =
+ new HashMap<String, Map<ModuleArchiveInfo, OSGiModuleDefinition> >();
+
+ /**
+ * Returns the OSGi repository.
+ *
+ * FIXME: initialize() is not called in the constructor to workaround
+ * the bootstrapping issue which will be resolved later.
+ */
+ public OSGiRepository(String name, URI source, Repository parent,
+ Map<String, String> config) throws IOException {
+ super(name, source, parent);
+ this.config = config;
+ }
+
+ @Override
+ public boolean isReadOnly() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsReload() {
+ return true;
+ }
+
+ private static final String CONTAINER = "container";
+
+ @Override
+ protected List<ModuleArchiveInfo> doInitialize() throws IOException {
+ // Start the OSGi runtime and initialize the map for installed bundles
+ // and ModuleDefinitions
+ OSGiRuntime.start(this.getSourceLocation(),
+ config.get(CONTAINER));
+ Set<Bundle> bundles = OSGiRuntime.getInstalledBundles();
+ List<ModuleArchiveInfo> moduleArchiveInfos = new ArrayList<ModuleArchiveInfo>();
+ for (Bundle bundle : bundles) {
+ moduleArchiveInfos.add(newModuleArchiveInfo(bundle));
+ // add the OSGiModuleDefinition to the internal data structure
+ addModuleDefinition(new OSGiModuleDefinition(this, bundle));
+ }
+ return moduleArchiveInfos;
+ }
+
+ private ModuleArchiveInfo newModuleArchiveInfo(Bundle bundle) {
+ return new JamModuleArchiveInfo(this,
+ bundle.getSymbolicName(),
+ BundleManifestMapper.getVersion(bundle),
+ null, /* XXX: platform neutral for now */
+ null,
+ bundle.getLocation(), /* XXX: is it a valid filename? */
+ bundle.getLastModified());
+ }
+
+ @Override
+ public final synchronized ModuleArchiveInfo install(final URI uri)
+ throws IOException {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new ModuleSystemPermission("installModuleArchive"));
+ }
+
+ if (uri == null) {
+ throw new NullPointerException("uri must not be null");
+ }
+
+ assertActive();
+ assertNotReadOnly();
+
+ // Checks to see if the file to be installed is one of the file format
+ // supported.
+ if (!uri.toURL().getFile().endsWith(".jar")) {
+ throw new ModuleFormatException(
+ "Only file format with .jar extension is supported");
+ }
+
+ try {
+ // Install the module archive under doPrivileged()
+ return AccessController.doPrivileged(
+ new PrivilegedExceptionAction<ModuleArchiveInfo>() {
+
+ public ModuleArchiveInfo run() throws Exception {
+ return installBundle(uri);
+ }
+ });
+ } catch (PrivilegedActionException e) {
+ if (e.getCause() instanceof IOException) {
+ throw (IOException) e.getCause();
+ } else if (e.getCause() instanceof IllegalStateException) {
+ throw (IllegalStateException) e.getCause();
+ } else {
+ throw new IOException("Unexpected exception has occurred", e);
+ }
+ }
+ }
+
+ /**
+ * Put the module archive into the repository cache, cook it, and update
+ * the internal data structure to reflect the change.
+ *
+ * @param uri bundle to be installed.
+ * @return module archive information
+ * @param IOException if there is an I/O exception occurs.
+ */
+ private final ModuleArchiveInfo installBundle(URI uri)
+ throws IOException {
+ // Installs a bundle and constructs a module archive info
+ Bundle bundle = OSGiRuntime.installBundle(uri);
+ ModuleArchiveInfo mai = newModuleArchiveInfo(bundle);
+
+ // Checks if a module definition already exists for a given module
+ // name and version (e.g. platform neutral vs platform specific module).
+ String key = mai.getName() + mai.getVersion();
+ Map<ModuleArchiveInfo, OSGiModuleDefinition> value = contentMapping.get(key);
+ if (value == null) {
+ // No module definition exists, and we should create one if the
+ // module archive supports the running platform and architecture.
+ // XXX: need to check for platform and architecture
+ value = new HashMap<ModuleArchiveInfo, OSGiModuleDefinition>();
+ if (true) {
+ // Constructs a module definition
+ OSGiModuleDefinition md = new OSGiModuleDefinition(this, bundle);
+ addModuleDefinition(md);
+ value.put(mai, md);
+ }
+ contentMapping.put(key, value);
+ } else {
+
+ // A module definition already exists for a given name and version
+ // (e.g. platform neutral vs platform specific module).
+
+ // XXX: do we replace the existing module definition with a new one
+ // if the newly installed module archive provides better
+ // platform binding?
+ //
+ // e.g. a platform neutral module is already installed and in use
+ // but now we just install a windows-x86 specific module (assuming
+ // we're running on Windows as well), should we swap the module
+ // definition of the platform neutral module with that of a newly
+ // installed one?
+ //
+ // For simplicity, the answer is no. Otherwise, the behavior could
+ // be very confusing and problematic.
+ }
+ addModuleArchiveInfo(mai);
+ return mai;
+ }
+
+ @Override
+ public final synchronized boolean uninstall(final ModuleArchiveInfo mai)
+ throws IOException {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new ModuleSystemPermission("uninstallModuleArchive"));
+ }
+
+ if (mai == null) {
+ throw new NullPointerException("mai must not be null");
+ }
+
+ assertActive();
+ assertNotReadOnly();
+
+ try {
+ // Uninstall the module archive under doPrivileged()
+ Boolean b = AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Boolean>() {
+
+ public Boolean run() throws Exception {
+ return uninstallBundle(mai);
+ }
+ });
+ return b.booleanValue();
+ } catch (PrivilegedActionException e) {
+ if (e.getCause() instanceof IOException) {
+ throw (IOException) e.getCause();
+ } else if (e.getCause() instanceof IllegalStateException) {
+ throw (IllegalStateException) e.getCause();
+ } else {
+ throw new IOException("Unexpected exception has occurred", e);
+ }
+ }
+ }
+
+ private boolean uninstallBundle(ModuleArchiveInfo mai) throws IOException {
+ // Checks if the module archive still exists.
+ String key = mai.getName() + mai.getVersion();
+ Map<ModuleArchiveInfo, OSGiModuleDefinition> value = contentMapping.get(key);
+ if (value == null) {
+ return false;
+ }
+
+ // TODO: assume it's a local jar file
+
+ // Remove the module archive if it is the same one that was installed,
+ // as determined by timestamp. Don't remove if the timestamp is
+ // different, as that could mean a file has been copied over the
+ // installed file.
+ File f = new File(mai.getFileName());
+
+ // XXX: Bundle's last modification date is zero
+ // Comment out the last modification date check for now.
+ /*
+ if (f.lastModified() != mai.getLastModified()) {
+ throw new IOException(
+ "Could not delete module archive " + mai.getFileName() +
+ " because the modification date " + f.lastModified() +
+ " was different than expected: " + mai.getLastModified());
+ }
+ */
+ if (f.isFile() && !f.delete()) {
+ throw new IOException(
+ "Could not delete module archive: " + mai.getFileName());
+ }
+
+ // Check if a module definition has been created for the module
+ // archive info
+ OSGiModuleDefinition md = value.get(mai);
+ if (md == null) {
+ // Module definition could be null if a platform neutral or
+ // platform-specific module with the same name and version
+ // already exists, but it's not the module archive that is
+ // being removed. In this case, there is no module definition
+ // to be removed from the internal data structure.
+ } else {
+ OSGiRuntime.uninstallBundle(md.getBundle());
+
+ // Removes the module archive and module definition mapping
+ // from internal data structure.
+ removeModuleDefinition(md);
+ contentMapping.remove(key);
+
+ try {
+ // Disables the module definition in the module system.
+ md.getModuleSystem().disableModuleDefinition(md);
+ } catch (UnsupportedOperationException uoe) {
+ // no-op
+ } catch (IllegalStateException ise) {
+ // no-op
+ }
+ try {
+ // Releases any module instance corresponding to the module
+ // definition in the module system
+ md.getModuleSystem().releaseModule(md);
+ } catch (UnsupportedOperationException uoe) {
+ // no-op
+ }
+
+ // It is certainly possible that the repository may have another
+ // module archive for the same module name/version. e.g. a platform
+ // specific module is uninstalled but there exists a platform
+ // neutral module in the repository. In this case, do we recreate
+ // the module definition for the platform neutral module and use
+ // it in the repository?
+ //
+ // For simplicity, the answer is no. If the user uninstalls a
+ // platform specific module from the repository, he/she would
+ // expect that no module definition for the given module
+ // name/version would be returned from the repository if it is
+ // searched through find(). If the repository returns a
+ // module definition of the platform neutral module instead,
+ // the behavior could be very confusing and problematic.
+ }
+
+ // Removes the module archive from the internal data structure
+ return removeModuleArchiveInfo(mai);
+ }
+
+ @Override
+ public final synchronized List<ModuleArchiveInfo> list() {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new ModuleSystemPermission("listModuleArchive"));
+ }
+ assertActive();
+ return getModuleArchiveInfos();
+ }
+
+ @Override
+ public final synchronized void reload() throws IOException {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null) {
+ sm.checkPermission(new ModuleSystemPermission("reloadRepository"));
+ }
+ assertActive();
+
+ if (!supportsReload()) {
+ throw new UnsupportedOperationException("Repository does not support reload");
+ }
+
+ try {
+ // Reload the module archives under doPrivileged()
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Boolean>() {
+
+ public Boolean run() throws Exception {
+ doReload();
+ return Boolean.TRUE;
+ }
+ });
+ } catch (PrivilegedActionException e) {
+ if (e.getCause() instanceof IOException) {
+ throw (IOException) e.getCause();
+ } else if (e.getCause() instanceof IllegalStateException) {
+ throw (IllegalStateException) e.getCause();
+ } else {
+ throw new IOException("Unexpected exception has occurred", e);
+ }
+ }
+ }
+
+ private void doReload() throws IOException {
+ // Build a list of modules to uninstall, and of modules currently
+ // installed that won't be uninstalled by this reload.
+ List<ModuleArchiveInfo> uninstallCandidates = new ArrayList<ModuleArchiveInfo>();
+ Set<File> existingBundles = new HashSet<File>();
+ for (ModuleArchiveInfo mai : list()) {
+ File f = new File(mai.getFileName());
+ long modTime = mai.getLastModified();
+ // Uninstall if source file is missing, or if it has been updated on disk.
+ if (!f.isFile() || (modTime != 0 && f.lastModified() != modTime)) {
+ uninstallCandidates.add(mai);
+ } else {
+ existingBundles.add(f);
+ }
+ }
+
+ // Remove modules from the internal data structures for which there
+ // is no corresponding JAM file in the source directory.
+ for (ModuleArchiveInfo mai : uninstallCandidates) {
+ // Removes the module archive and the corresponding module
+ // definition from the internal data structure so this
+ // repository won't recognize it anymore.
+ uninstallBundle(mai);
+ }
+
+ File bundleLocation = new File(getSourceLocation().toURL().getFile());
+ for (File file : bundleLocation.listFiles()) {
+ if (!existingBundles.contains(file)) {
+ if (file.getName().endsWith(".jar")) {
+ installBundle(file.toURI());
+ }
+ }
+ }
+ }
+
+ protected void doShutdown() throws IOException {
+ // XXX: To be implemented
+ }
+
+ private void assertActive() throws IllegalStateException {
+ if (!isActive()) {
+ throw new IllegalStateException("OSGi repository is not active.");
+ }
+ }
+
+ private void assertNotActive() throws IllegalStateException {
+ if (isActive()) {
+ throw new IllegalStateException("OSGi repository is active.");
+ }
+ }
+
+ private void assertNotReadOnly() throws IllegalStateException {
+ if (isReadOnly()) {
+ throw new UnsupportedOperationException("OSGi repository is read-only.");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/module/osgi/OSGiRuntime.java Tue Jul 22 17:55:50 2008 -0700
@@ -0,0 +1,319 @@
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.module.osgi;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.packageadmin.RequiredBundle;
+
+import org.apache.felix.framework.Felix;
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.StringMap;
+import org.apache.felix.framework.cache.BundleCache;
+import org.apache.felix.main.AutoActivator;
+import org.apache.felix.main.Main;
+
+/**
+ * OSGi runtime provides the API to start the container and
+ * access the OSGi representations wrapping the container implementation
+ * specific API.
+ *
+ * XXX: Use reflection API to support different OSGi container implementation
+ *
+ */
+class OSGiRuntime implements BundleActivator {
+
+ private static final OSGiRuntime runtime =
+ new OSGiRuntime();
+
+ private static URI bundleSourceLocation;
+ private static Felix felix;
+ private static boolean debug = System.getProperty("java.module.debug") != null;
+
+ private BundleContext context = null;
+ private PackageAdmin packageAdmin = null;
+
+ private OSGiRuntime() {
+ }
+
+ public void start(BundleContext context) {
+ this.context = context;
+ this.packageAdmin = (PackageAdmin)
+ context.getService(
+ context.getServiceReference(PackageAdmin.class.getName()));
+ }
+
+ public void stop(BundleContext context) {
+ // the framework is being stopped
+ //
+ // XXX: OSGi module system will stop working.
+ context = null;
+ }
+
+ BundleContext getContext() {
+ return context;
+ }
+
+ PackageAdmin getPackageAdmin() {
+ return packageAdmin;
+ }
+
+ /**
+ * Start the OSGi container.
+ */
+ static void start(URI source, String osgiContainer) throws IOException {
+ if (debug) {
+ System.out.println("Starting the OSGi runtime at source = " +
+ source + " container = " + osgiContainer);
+ }
+
+ bundleSourceLocation = source;
+
+ File container = null;
+ if (osgiContainer != null) {
+ container = new File(URI.create(osgiContainer));
+ } else {
+ // Determine whether the OSGi container is in the class path
+ String classpath = System.getProperty("java.class.path");
+ int index = classpath.toLowerCase().indexOf("felix.jar");
+ int start = classpath.lastIndexOf(File.pathSeparator, index) + 1;
+ if (index >= start)
+ {
+ // Get the path of the felix.jar file.
+ String jarLocation = classpath.substring(start, index + 9);
+ container = new File(jarLocation);
+ } else {
+ // No OSGi container
+ throw new IOException("Felix container doesn't exist");
+ }
+ }
+
+ // Read configuration properties.
+ Properties configProps = initConfigProperties(container);
+
+ try
+ {
+ // Create a list for custom framework activators and
+ // add an instance of the auto-activator it for processing
+ // auto-install and auto-start properties.
+ List<BundleActivator> list = new ArrayList<BundleActivator>();
+ list.add(new AutoActivator(configProps));
+ list.add(runtime);
+
+ // Create a case-insensitive property map.
+ Map configMap = new StringMap(configProps, false);
+ // Create an instance of the framework.
+ felix = new Felix(configMap, list);
+ felix.start();
+ } catch (BundleException e) {
+ System.err.println("Failed to create the Felix framework");
+ throw new IOException(e);
+ }
+ }
+
+ // Initialize the configuration properties for the OSGi container
+ private static Properties initConfigProperties(File container) throws IOException {
+ // (1) Load system properties.
+ Main.loadSystemProperties();
+
+ // Workaround: Felix implementation assumes that felix.jar is on
+ // the classpath ("java.class.path" system property) for locating
+ // the default configuration properties file.
+ // So set the "felix.config.properties" property to workaround it
+ if (System.getProperty("felix.config.properties") == null) {
+ File confDir = new File(container.getParentFile().getParentFile(), "conf");
+ File f = new File(confDir, "config.properties");
+ if (f.exists()) {
+ System.setProperty("felix.config.properties", f.toURI().toString());
+ }
+ }
+
+ // (2) Read configuration properties.
+ Properties configProps = Main.loadConfigProperties();
+
+ // (3) Copy framework properties from the system properties.
+ Main.copySystemProperties(configProps);
+
+ // (4) See if the profile name and directory property was specified.
+ String profileName = configProps.getProperty(BundleCache.CACHE_PROFILE_PROP);
+ String profileDirName = configProps.getProperty(BundleCache.CACHE_PROFILE_DIR_PROP);
+
+ if ((profileName == null || profileName.length() == 0) &&
+ (profileDirName == null || profileDirName.length() == 0)) {
+ throw new IOException("Fail to start the OSGi container: " +
+ "Profile name or directory property is not specified");
+ }
+
+ // Configure the Felix instance to be embedded.
+ configProps.put(FelixConstants.EMBEDDED_EXECUTION_PROP, "true");
+
+ // Auto start the bundles listed in the source location
+ StringBuilder sb = new StringBuilder();
+ File p = new File(bundleSourceLocation);
+ for (File file : p.listFiles()) {
+ sb.append(file.toURI());
+ sb.append(" ");
+ }
+ if (sb.length() > 0) {
+ configProps.put(AutoActivator.AUTO_INSTALL_PROP + ".1", sb.toString());
+ }
+
+ if (debug) {
+ configProps.put("felix.log.level", "4");
+ StringBuilder autostart = new StringBuilder();
+ File dir = new File(container.getParentFile().getParentFile(), "bundle");
+ for (File file : dir.listFiles()) {
+ System.out.println(file);
+ autostart.append(file.toURI());
+ autostart.append(" ");
+ }
+ if (autostart.length() > 0) {
+ configProps.put(AutoActivator.AUTO_START_PROP + ".1", autostart.toString());
+ }
+ }
+
+ return configProps;
+ }
+
+ static Bundle installBundle(URI uri) throws IOException {
+ try {
+ return runtime.getContext().installBundle(uri.toString());
+ } catch (BundleException e) {
+ throw new IOException(e);
+ }
+ }
+
+ static void uninstallBundle(Bundle bundle) throws IOException {
+ try {
+ bundle.uninstall();
+ } catch (BundleException e) {
+ throw new IOException(e);
+ }
+ }
+
+ static Set<Bundle> getInstalledBundles() {
+ Set<Bundle> installedBundles = new HashSet<Bundle>();
+ Bundle[] bundles = runtime.getContext().getBundles();
+
+ for (Bundle bundle : bundles) {
+ if (bundle.getBundleId() == 0) {
+ // Ignore system bundle.
+ continue;
+ }
+ if (bundle.getState() == Bundle.UNINSTALLED) {
+ // Ignore bundle that is uninstalled.
+ continue;
+ }
+ installedBundles.add(bundle);
+ }
+
+ return installedBundles;
+ }
+
+ static Bundle getExportingBundle(Bundle bundle, String packageName) {
+ ExportedPackage[] epkgs = runtime.getPackageAdmin().getExportedPackages(packageName);
+ if (epkgs != null) {
+ for (ExportedPackage ep : epkgs) {
+ Bundle[] importingBundles = ep.getImportingBundles();
+ if (importingBundles == null) {
+ return null;
+ }
+ for (Bundle b : importingBundles) {
+ if (b.getBundleId() == bundle.getBundleId()) {
+ return ep.getExportingBundle();
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ static Bundle getRequiredBundle(Bundle bundle, String bundleSymbolicName) {
+ /*
+ * TODO: Felix has not implemented getRequiredBundles() which just returns null
+ *
+ RequiredBundle[] reqBundles = runtime.getPackageAdmin().getRequiredBundles(bundleSymbolicName);
+ if (reqBundles != null) {
+ for (RequiredBundle rb : reqBundles) {
+ Bundle[] requiringBundles = rb.getRequiringBundles();
+ if (requiringBundles == null) {
+ return null;
+ }
+ for (Bundle b : requiringBundles) {
+ if (b.getBundleId() == bundle.getBundleId()) {
+ return rb.getBundle();
+ }
+ }
+ }
+ }
+ */
+ Bundle[] bundles = runtime.getPackageAdmin().getBundles(bundleSymbolicName, null);
+ if (bundles != null && bundles.length >= 1) {
+ if (bundles.length == 1) {
+ return bundles[0];
+ }
+ throw new AssertionError("Required-Bundle not supported yet");
+ }
+ return null;
+ }
+
+ static Set<ExportedPackage> getExportedPackages(Bundle bundle) {
+ Set<ExportedPackage> exports = new HashSet<ExportedPackage>();
+ ExportedPackage[] epkgs = runtime.getPackageAdmin().getExportedPackages(bundle);
+ if (epkgs != null) {
+ for (ExportedPackage ep : epkgs) {
+ String packageName = ep.getName();
+ if (packageName.equals(".") ||
+ packageName.startsWith("META-INF.") ||
+ packageName.startsWith("license")) {
+ continue;
+ }
+ exports.add(ep);
+ }
+ }
+ return exports;
+ }
+}