Modified the Felix API to aligned with the proposed standard OSGi framework
API. The main changes implemented here are the new constructor that takes
a single Map argument and the addition of the init() and waitForStop(),
the latter resplaces stopAndWait(). This is not complete, but it is a good
start. The framework instance can now be stopped and restarted. (FELIX-753)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@702992 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
index b60723c..08c9ebc 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
@@ -29,9 +29,9 @@
{
private final long m_id;
private Felix m_felix = null;
- private BundleInfo m_info = null;
+ private RegularBundleInfo m_info = null;
- protected BundleImpl(Felix felix, BundleInfo info)
+ protected BundleImpl(Felix felix, RegularBundleInfo info)
{
m_felix = felix;
m_info = info;
@@ -46,7 +46,7 @@
/*
* Only used when refreshing a bundle.
**/
- /* package private */ void setInfo(BundleInfo info)
+ /* package private */ void setInfo(RegularBundleInfo info)
{
m_info = info;
}
diff --git a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
index 7449830..a229e53 100644
--- a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
+++ b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
@@ -19,7 +19,7 @@
package org.apache.felix.framework;
import java.io.IOException;
-import java.net.InetAddress;
+import java.net.InetAddress;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
@@ -57,39 +57,39 @@
import org.osgi.framework.Constants;
/**
- * The ExtensionManager class is used in several ways.
+ * The ExtensionManager class is used in several ways.
* <p>
- * First, a private instance is added (as URL with the instance as
- * URLStreamHandler) to the classloader that loaded the class.
- * It is assumed that this is an instance of URLClassloader (if not extension
- * bundles will not work). Subsequently, extension bundles can be managed by
- * instances of this class (their will be one instance per framework instance).
+ * First, a private instance is added (as URL with the instance as
+ * URLStreamHandler) to the classloader that loaded the class.
+ * It is assumed that this is an instance of URLClassloader (if not extension
+ * bundles will not work). Subsequently, extension bundles can be managed by
+ * instances of this class (their will be one instance per framework instance).
* </p>
* <p>
* Second, it is used as module definition of the systembundle. Added extension
- * bundles with exported packages will contribute their exports to the
+ * bundles with exported packages will contribute their exports to the
* systembundle export.
* </p>
* <p>
- * Third, it is used as content loader of the systembundle. Added extension
+ * Third, it is used as content loader of the systembundle. Added extension
* bundles exports will be available via this loader.
* </p>
*/
-// The general approach is to have one private static instance that we register
+// The general approach is to have one private static instance that we register
// with the parent classloader and one instance per framework instance that
// keeps track of extension bundles and systembundle exports for that framework
// instance.
class ExtensionManager extends URLStreamHandler implements IModuleDefinition, IContentLoader, IContent
{
// The private instance that is added to Felix.class.getClassLoader() -
- // will be null if extension bundles are not supported (i.e., we are not
+ // will be null if extension bundles are not supported (i.e., we are not
// loaded by an instance of URLClassLoader)
static final ExtensionManager m_extensionManager;
-
+
static
{
- // We use the secure action of Felix to add a new instance to the parent
- // classloader.
+ // We use the secure action of Felix to add a new instance to the parent
+ // classloader.
ExtensionManager extensionManager = new ExtensionManager();
try
{
@@ -98,16 +98,16 @@
"felix://extensions/", extensionManager),
Felix.class.getClassLoader());
}
- catch (Exception ex)
+ catch (Exception ex)
{
- // extension bundles will not be supported.
+ // extension bundles will not be supported.
extensionManager = null;
}
m_extensionManager = extensionManager;
}
private Logger m_logger = null;
- private BundleInfo m_systemBundleInfo = null;
+ private BundleInfo m_sbi = null;
private ICapability[] m_capabilities = null;
private Set m_exportNames = null;
private ISearchPolicy m_searchPolicy = null;
@@ -128,28 +128,28 @@
/**
* This constructor is used to create one instance per framework instance.
- * The general approach is to have one private static instance that we register
+ * The general approach is to have one private static instance that we register
* with the parent classloader and one instance per framework instance that
* keeps track of extension bundles and systembundle exports for that framework
* instance.
- *
+ *
* @param logger the logger to use.
* @param config the configuration to read properties from.
* @param systemBundleInfo the info to change if we need to add exports.
*/
- ExtensionManager(Logger logger, Map configMap, BundleInfo systemBundleInfo)
+ ExtensionManager(Logger logger, Map configMap, BundleInfo sbi)
{
m_extensions = null;
m_names = null;
m_sourceToExtensions = null;
m_logger = logger;
- m_systemBundleInfo = systemBundleInfo;
+ m_sbi = sbi;
// TODO: FRAMEWORK - Not all of this stuff really belongs here, probably only exports.
// Populate system bundle header map.
// Note: This is a reference to the actual header map,
// so these changes are saved. Kind of hacky.
- Map map = ((SystemBundleArchive) m_systemBundleInfo.getArchive()).getManifestHeader(0);
+ Map map = m_sbi.getCurrentHeader();
// Initialize header map as a case insensitive map.
map.put(FelixConstants.BUNDLE_VERSION,
configMap.get(FelixConstants.FELIX_VERSION_PROPERTY));
@@ -249,19 +249,19 @@
/**
* Add an extension bundle. The bundle will be added to the parent classloader
- * and it's exported packages will be added to the module definition
- * exports of this instance. Subsequently, they are available form the
+ * and it's exported packages will be added to the module definition
+ * exports of this instance. Subsequently, they are available form the
* instance in it's role as content loader.
- *
+ *
* @param felix the framework instance the given extension bundle comes from.
* @param bundle the extension bundle to add.
* @throws BundleException if extension bundles are not supported or this is
* not a framework extension.
- * @throws SecurityException if the caller does not have the needed
+ * @throws SecurityException if the caller does not have the needed
* AdminPermission.EXTENSIONLIFECYCLE and security is enabled.
* @throws Exception in case something goes wrong.
*/
- void addExtensionBundle(Felix felix, FelixBundle bundle)
+ void addExtensionBundle(Felix felix, FelixBundle bundle)
throws SecurityException, BundleException, Exception
{
Object sm = System.getSecurityManager();
@@ -311,7 +311,7 @@
}
// Add the bundle as extension if we support extensions
- if (m_extensionManager != null)
+ if (m_extensionManager != null)
{
// This needs to be the private instance.
m_extensionManager.addExtension(felix, bundle);
@@ -338,14 +338,14 @@
{
felix.releaseBundleLock(felix);
}
-
+
bundle.getInfo().setState(Bundle.RESOLVED);
}
/**
* This is a Felix specific extension mechanism that allows extension bundles
* to have activators and be started via this method.
- *
+ *
* @param felix the framework instance the extension bundle is installed in.
* @param bundle the extension bundle to start if it has a Felix specific activator.
*/
@@ -354,7 +354,7 @@
String activatorClass = (String)
bundle.getInfo().getCurrentHeader().get(
FelixConstants.FELIX_EXTENSION_ACTIVATOR);
-
+
if (activatorClass != null)
{
try
@@ -362,13 +362,14 @@
BundleActivator activator = (BundleActivator)
felix.getClass().getClassLoader().loadClass(
activatorClass.trim()).newInstance();
-
+
+// TODO: KARL - This is kind of hacky, can we improve it?
felix.m_activatorList.add(activator);
-
- BundleContext context = m_systemBundleInfo.getBundleContext();
-
+
+ BundleContext context = m_sbi.getBundleContext();
+
bundle.getInfo().setBundleContext(context);
-
+
if (felix.getInfo().getState() == Bundle.ACTIVE)
{
Felix.m_secureAction.startActivator(activator, context);
@@ -382,15 +383,15 @@
}
}
}
-
+
/**
* Remove all extension registered by the given framework instance. Note, it
* is not possible to unregister allready loaded classes form those extensions.
* That is why the spec requires a JVM restart.
- *
- * @param felix the framework instance whose extensions need to be unregistered.
+ *
+ * @param felix the framework instance whose extensions need to be unregistered.
*/
- void removeExtensions(Felix felix)
+ void removeExtensions(Felix felix)
{
if (m_extensionManager != null)
{
@@ -412,7 +413,7 @@
// Note: This is a reference to the actual header map,
// so these changes are saved. Kind of hacky.
- Map map = ((SystemBundleArchive) m_systemBundleInfo.getArchive()).getManifestHeader(0);
+ Map map = m_sbi.getCurrentHeader();
map.put(Constants.EXPORT_PACKAGE, convertCapabilitiesToHeaders(map));
}
@@ -461,11 +462,11 @@
return exportSB.toString();
}
-
+
//
// IContentLoader
//
-
+
public void open()
{
// Nothing needed here.
@@ -560,7 +561,7 @@
// No native libs associated with the system bundle.
return null;
}
-
+
//
// Classpath Extension
//
@@ -591,10 +592,10 @@
throw new IOException("Resource not provided by any extension!");
}
- protected InetAddress getHostAddress(URL u)
- {
- // the extension URLs do not address real hosts
- return null;
+ protected InetAddress getHostAddress(URL u)
+ {
+ // the extension URLs do not address real hosts
+ return null;
}
private synchronized void addExtension(Object source, Bundle extension)
@@ -642,16 +643,16 @@
}
}
- public Enumeration getEntries()
+ public Enumeration getEntries()
{
return new Enumeration()
{
- public boolean hasMoreElements()
+ public boolean hasMoreElements()
{
return false;
}
- public Object nextElement() throws NoSuchElementException
+ public Object nextElement() throws NoSuchElementException
{
throw new NoSuchElementException();
}
@@ -662,12 +663,12 @@
return false;
}
- public byte[] getEntryAsBytes(String name)
+ public byte[] getEntryAsBytes(String name)
{
return null;
}
- public InputStream getEntryAsStream(String name) throws IOException
+ public InputStream getEntryAsStream(String name) throws IOException
{
return null;
}
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index a368c7d..c21c81b 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -33,7 +33,7 @@
import org.osgi.service.packageadmin.ExportedPackage;
import org.osgi.service.startlevel.StartLevel;
-public class Felix extends FelixBundle
+public class Felix extends FelixBundle implements SystemBundle
{
// The secure action used to do privileged calls
static SecureAction m_secureAction = new SecureAction();
@@ -65,8 +65,8 @@
private Object[] m_installRequestLock_Priority1 = new Object[0];
// Maps a bundle location to a bundle.
- private HashMap m_installedBundleMap = new HashMap();
- private SortedMap m_installedBundleIndex = new TreeMap();
+ private HashMap m_installedBundleMap;
+ private SortedMap m_installedBundleIndex;
// This lock must be acquired to modify m_installedBundleMap;
// to help avoid deadlock this lock as priority 2 and should
// be acquired before locks with lower priority.
@@ -87,7 +87,8 @@
private BundleCache m_cache = null;
// System bundle bundle info instance.
- private BundleInfo m_systemBundleInfo = null;
+ private BundleInfo m_sbi;
+
// System bundle activator list.
List m_activatorList = null;
@@ -110,33 +111,16 @@
// Shutdown thread.
private Thread m_shutdownThread = null;
-
- /**
- * Creates a new Felix framework instance with a default logger.
- *
- * @param configMutableMap An map for obtaining configuration properties,
- * may be <tt>null</tt>.
- * @param activatorList A list of System Bundle activators.
- *
- * @see #Felix(Logger, Map, List)
- */
- public Felix(Map configMutableMap, List activatorList)
- {
- this(null, configMutableMap, activatorList);
- }
+ private ThreadGate m_shutdownGate = null;
/**
* <p>
- * This method creates the framework instance; instances of the framework
- * are not active until they are started. The constructor accepts a
- * <tt>Map</tt> instance that will be used to obtain configuration or
- * framework properties. Configuration properties are used internally by
- * the framework and its extensions to alter its default behavior.
- * Framework properties are used by bundles and are accessible from
- * <tt>BundleContext.getProperty()</tt>. This map instance is used
- * directly (i.e., it is not copied), which means that it is possible to
- * change the property values externally at run time, but this is strongly
- * discouraged for the framework's configuration properties.
+ * This constructor creates a framework instance with a specified <tt>Map</tt>
+ * of configuration properties. Configuration properties are used internally
+ * by the framework to alter its default behavior. The configuration properties
+ * should have a <tt>String</tt> key and an <tt>Object</tt> value. The passed
+ * in <tt>Map</tt> is copied by the framework and all keys are converted to
+ * <tt>String</tt>s.
* </p>
* <p>
* Configuration properties are the sole means to configure the framework's
@@ -146,15 +130,19 @@
* <tt>Map</tt> instance for any and all configuration properties. It is
* possible to specify a <tt>null</tt> for the configuration property map,
* in which case the framework will use its default behavior in all cases.
- * However, if the
- * <a href="cache/DefaultBundleCache.html"><tt>DefaulBundleCache</tt></a>
- * is used, then at a minimum a profile name or profile directory must
- * be specified.
* </p>
* <p>
* The following configuration properties can be specified:
* </p>
* <ul>
+ * <li><tt>felix.systembundle.activators</tt> - A <tt>List</tt> of
+ * <tt>BundleActivator</tt> instances that are started/stopped when
+ * the System Bundle is started/stopped; the specified instances will
+ * receive the System Bundle's <tt>BundleContext</tt> when invoked.
+ * </li>
+ * <li><tt>felix.log.logger</tt> - An instance of <tt>Logger</tt> that the
+ * framework uses as its default logger.
+ * </li>
* <li><tt>felix.log.level</tt> - An integer value indicating the degree
* of logging reported by the framework; the higher the value the more
* logging is reported. If zero ('0') is specified, then logging is
@@ -175,28 +163,37 @@
* service will result in the <tt>URL.setURLStreamHandlerFactory()</tt>
* and <tt>URLConnection.setContentHandlerFactory()</tt> being called.
* </li>
- * <li><tt>felix.embedded.execution</tt> - Flag to indicate whether
- * the framework is embedded into a host application; the default value is
- * "<tt>false</tt>". If this flag is "<tt>true</tt>" then the framework
- * will not called <tt>System.exit()</tt> upon termination.
+ * <li><tt>felix.cache.bufsize</tt> - Sets the buffer size to be used by
+ * the cache; the default value is 4096. The integer
+ * value of this string provides control over the size of the
+ * internal buffer of the disk cache for performance reasons.
* </li>
- * <li><tt>felix.strict.osgi</tt> - Flag to indicate whether the framework is
- * running in strict OSGi mode; the default value is "<tt>true</tt>".
- * If this flag is "<tt>false</tt>" it enables a non-OSGi-compliant
- * feature by persisting <tt>BundleActivator</tt>s that implement
- * <tt>Serializable</tt>. This feature is not recommended since
- * it is non-compliant.
+ * <li><tt>felix.cache.dir</tt> - Sets the directory to be used by the
+ * cache as its cache directory. The cache directory is where all
+ * profile directories are stored and a profile directory is where a
+ * set of installed bundles are stored. By default, the cache
+ * directory is <tt>.felix</tt> in the user's home directory. If
+ * this property is specified, then its value will be used as the cache
+ * directory instead of <tt>.felix</tt>. This directory will be created
+ * if it does not exist.
+ * </li>
+ * <li><tt>felix.cache.profile</tt> - Sets the profile name that will be
+ * used to create a profile directory inside of the cache directory.
+ * The created directory will contained all installed bundles associated
+ * with the profile.
+ * </li>
+ * <li><tt>felix.cache.profiledir</tt> - Sets the directory to use as the
+ * profile directory for the bundle cache; by default the profile
+ * name is used to create a directory in the <tt>.felix</tt> cache
+ * directory. If this property is specified, then the cache directory
+ * and profile name properties are ignored. The specified value of this
+ * property is used directly as the directory to contain all cached
+ * bundles. If this property is set, it is not necessary to set the
+ * cache directory or profile name properties. This directory will be
+ * created if it does not exist.
* </li>
* </ul>
* <p>
- * Besides the above framework configuration properties, it is also
- * possible to specify properties for the bundle cache. The available
- * bundle cache properties depend on the cache implementation
- * being used. For the properties of the default bundle cache, refer to the
- * <a href="cache/DefaultBundleCache.html"><tt>DefaulBundleCache</tt></a>
- * API documentation.
- * </p>
- * <p>
* The <a href="Main.html"><tt>Main</tt></a> class implements some
* functionality for default property file handling, which makes it
* possible to specify configuration properties and framework properties
@@ -205,27 +202,36 @@
* able to take advantage of the features it provides; refer to its
* class documentation for more information.
* </p>
+ * <p>
+ * The framework is not actually started until the <tt>start()</tt> method
+ * is called.
+ * </p>
*
- * @param logger The logger for use by the framework or <code>null</code>
- * use the default logger.
- * @param configMutableMap A map for obtaining configuration properties,
+ * @param configMap A map for obtaining configuration properties,
* may be <tt>null</tt>.
- * @param activatorList A list of System Bundle activators.
**/
- public Felix(Logger logger, Map configMutableMap, List activatorList)
+ public Felix(Map configMap)
{
- // Initialize member variables.
- m_configMutableMap = (configMutableMap == null)
- ? new StringMap(false) : configMutableMap;
+ // Copy the configuration properties; convert keys to strings.
+ m_configMutableMap = new StringMap(false);
+ if (configMap != null)
+ {
+ for (Iterator i = configMap.entrySet().iterator(); i.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) i.next();
+ m_configMutableMap.put(entry.getKey().toString(), entry.getValue());
+ }
+ }
m_configMap = createUnmodifiableMap(m_configMutableMap);
- m_activatorList = (activatorList == null) ? new ArrayList() : activatorList;
// Create logger with appropriate log level. Even though the
// logger needs the system bundle's context for tracking log
// services, it is created now because it is needed before
- // the system bundle is created. The system bundle's context
- // will be set below after the system bundle is created.\
- m_logger = (logger == null) ? new Logger() : logger;
+ // the system bundle is activated. The system bundle's context
+ // will be set in the init() method after the system bundle
+ // is activated.
+ m_logger = (Logger) m_configMutableMap.get(FelixConstants.LOG_LOGGER_PROP);
+ m_logger = (m_logger == null) ? new Logger() : m_logger;
try
{
m_logger.setLogLevel(
@@ -240,46 +246,98 @@
// Initialize framework properties.
initializeFrameworkProperties();
- // Create the bundle cache since we need it for the system bundle
- // archive, which in turn is needed by the system bundle info,
- // which we need to keep track of the framework state.
+ // Create default bundle stream handler.
+ m_bundleStreamHandler = new URLHandlersBundleStreamHandler(this);
+
+ // Create search policy for module loader.
+ m_policyCore = new R4SearchPolicyCore(m_logger, m_configMap);
+
+ // Add a resolver listener to the search policy
+ // so that we will be notified when modules are resolved
+ // in order to update the bundle state.
+ m_policyCore.addResolverListener(new ResolveListener() {
+ public void moduleResolved(ModuleEvent event)
+ {
+ FelixBundle bundle = null;
+ try
+ {
+ long id = Util.getBundleIdFromModuleId(
+ event.getModule().getId());
+ if (id > 0)
+ {
+ // Update the bundle's state to resolved when the
+ // current module is resolved; just ignore resolve
+ // events for older revisions since this only occurs
+ // when an update is done on an unresolved bundle
+ // and there was no refresh performed.
+ bundle = (FelixBundle) getBundle(id);
+
+ // Lock the bundle first.
+ try
+ {
+ acquireBundleLock(bundle);
+ if (bundle.getInfo().getCurrentModule() == event.getModule())
+ {
+ if (bundle.getInfo().getState() != Bundle.INSTALLED)
+ {
+ m_logger.log(
+ Logger.LOG_WARNING,
+ "Received a resolve event for a bundle that has already been resolved.");
+ }
+ else
+ {
+ bundle.getInfo().setState(Bundle.RESOLVED);
+ fireBundleEvent(BundleEvent.RESOLVED, bundle);
+ }
+ }
+ }
+ finally
+ {
+ releaseBundleLock(bundle);
+ }
+ }
+ }
+ catch (NumberFormatException ex)
+ {
+ // Ignore.
+ }
+ }
+
+ public void moduleUnresolved(ModuleEvent event)
+ {
+ // We can ignore this, because the only time it
+ // should happen is when a refresh occurs. The
+ // refresh operation resets the bundle's state
+ // by calling BundleInfo.reset(), thus it is not
+ // necessary for us to reset the bundle's state
+ // here.
+ }
+ });
+
+ // Create the module factory and attach it to the search policy.
+ m_factory = new ModuleFactoryImpl(m_logger);
+ m_policyCore.setModuleFactory(m_factory);
+
+ // Create the system bundle info object, which will hold state info.
+ m_sbi = new SystemBundleInfo();
+ // Create the extension manager, which we will use as the module
+ // definition for creating the system bundle module.
+ m_extensionManager = new ExtensionManager(m_logger, m_configMap, m_sbi);
+ m_sbi.addModule(m_factory.createModule("0", m_extensionManager));
+ // Set the extension manager as the content loader for the system
+ // bundle module.
+ m_extensionManager.setSearchPolicy(
+ new R4SearchPolicy(m_policyCore, m_sbi.getCurrentModule()));
+ m_factory.setContentLoader(m_sbi.getCurrentModule(), m_extensionManager);
+ // Lastly, set the system bundle's protection domain.
try
{
- m_cache = new BundleCache(m_logger, m_configMap);
+ addSecurity(this);
}
catch (Exception ex)
{
- System.err.println("Error creating bundle cache:");
- ex.printStackTrace();
-
- // Only shutdown the JVM if the framework is running stand-alone.
- String embedded = (String) m_configMap.get(
- FelixConstants.EMBEDDED_EXECUTION_PROP);
- boolean isEmbedded = (embedded == null)
- ? false : embedded.equals("true");
- if (!isEmbedded)
- {
- m_secureAction.exit(-1);
- }
- else
- {
- throw new RuntimeException(ex.toString());
- }
+ // This should not happen
}
-
- // Create the module factory and add the system bundle
- // module to it, since we need the system bundle info
- // object to keep track of the framework state.
- m_factory = new ModuleFactoryImpl(m_logger);
- m_systemBundleInfo = new BundleInfo(
- m_logger, new SystemBundleArchive(m_cache), null);
- m_extensionManager =
- new ExtensionManager(m_logger, m_configMap, m_systemBundleInfo);
-// TODO: REFACTOR - Right now this module added event is getting lost
-// by R4SearchPolicyCore, which uses it to populate the avail package
-// map. Not super important, since it gets the resolved event.
- m_systemBundleInfo.addModule(
- m_factory.createModule("0", m_extensionManager));
}
private Map createUnmodifiableMap(Map mutableMap)
@@ -308,17 +366,13 @@
/* package private */ BundleInfo getInfo()
{
- return m_systemBundleInfo;
+ return m_sbi;
}
public BundleContext getBundleContext()
{
// TODO: SECURITY - We need a security check here.
- if (m_systemBundleInfo != null)
- {
- return m_systemBundleInfo.getBundleContext();
- }
- return null;
+ return m_sbi.getBundleContext();
}
public long getBundleId()
@@ -405,11 +459,7 @@
public long getLastModified()
{
- if (m_systemBundleInfo != null)
- {
- return m_systemBundleInfo.getLastModified();
- }
- return -1;
+ return m_sbi.getLastModified();
}
public String getLocation()
@@ -561,7 +611,7 @@
public int getState()
{
- return m_systemBundleInfo.getState();
+ return m_sbi.getState();
}
public String getSymbolicName()
@@ -599,247 +649,54 @@
return loadBundleClass(this, name);
}
- public synchronized void start() throws BundleException
+ /**
+ * This method initializes the framework, which is comprised of resolving
+ * the system bundle, reloading any cached bundles, and activating the system
+ * bundle. The framework is left in the <tt>Bundle.STARTING</tt> state and
+ * reloaded bundles are in the <tt>Bundle.INSTALLED</tt> state. After
+ * successfully invoking this method, <tt>getBundleContext()</tt> will
+ * return a valid <tt>BundleContext</tt> for the system bundle. To finish
+ * starting the framework, invoke the <tt>start()</tt> method.
+ *
+ * @throws org.osgi.framework.BundleException if any error occurs.
+ **/
+ public synchronized void init() throws BundleException
{
- // The system bundle is only started once and it
- // is started by the framework.
- if (getState() == Bundle.ACTIVE)
+ // The system bundle can only be initialized if it currently isn't started.
+ final int state = m_sbi.getState();
+ if ((state == Bundle.INSTALLED) || (state == Bundle.RESOLVED))
{
- return;
- }
- else if (m_systemBundleInfo.getState() != Bundle.INSTALLED)
- {
- throw new IllegalStateException("Invalid framework state: " + m_systemBundleInfo.getState());
- }
+ // Get any system bundle activators.
+ m_activatorList = (List) m_configMutableMap.get(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP);
+ m_activatorList = (m_activatorList == null) ? new ArrayList() : new ArrayList(m_activatorList);
- // The framework is now in its startup sequence.
- m_systemBundleInfo.setState(Bundle.STARTING);
+ // Initialize event dispatcher.
+ m_dispatcher = EventDispatcher.start(m_logger);
- // Create default bundle stream handler.
- m_bundleStreamHandler = new URLHandlersBundleStreamHandler(this);
-
- // Create service registry.
- m_registry = new ServiceRegistry(m_logger);
- // Add a listener to the service registry; this is
- // used to distribute service registry events to
- // service listeners.
- m_registry.addServiceListener(new ServiceListener() {
- public void serviceChanged(ServiceEvent event)
- {
- fireServiceEvent(event);
- }
- });
-
- // Create search policy for module loader.
- m_policyCore = new R4SearchPolicyCore(m_logger, m_configMap);
-
- // Add a resolver listener to the search policy
- // so that we will be notified when modules are resolved
- // in order to update the bundle state.
- m_policyCore.addResolverListener(new ResolveListener() {
- public void moduleResolved(ModuleEvent event)
- {
- FelixBundle bundle = null;
- try
- {
- long id = Util.getBundleIdFromModuleId(
- event.getModule().getId());
- if (id > 0)
- {
- // Update the bundle's state to resolved when the
- // current module is resolved; just ignore resolve
- // events for older revisions since this only occurs
- // when an update is done on an unresolved bundle
- // and there was no refresh performed.
- bundle = (FelixBundle) getBundle(id);
-
- // Lock the bundle first.
- try
- {
- acquireBundleLock(bundle);
- if (bundle.getInfo().getCurrentModule() == event.getModule())
- {
- if (bundle.getInfo().getState() != Bundle.INSTALLED)
- {
- m_logger.log(
- Logger.LOG_WARNING,
- "Received a resolve event for a bundle that has already been resolved.");
- }
- else
- {
- bundle.getInfo().setState(Bundle.RESOLVED);
- fireBundleEvent(BundleEvent.RESOLVED, bundle);
- }
- }
- }
- finally
- {
- releaseBundleLock(bundle);
- }
- }
- }
- catch (NumberFormatException ex)
- {
- // Ignore.
- }
- }
-
- public void moduleUnresolved(ModuleEvent event)
- {
- // We can ignore this, because the only time it
- // should happen is when a refresh occurs. The
- // refresh operation resets the bundle's state
- // by calling BundleInfo.reset(), thus it is not
- // necessary for us to reset the bundle's state
- // here.
- }
- });
-
- m_policyCore.setModuleFactory(m_factory);
-
- // Initialize event dispatcher.
- m_dispatcher = EventDispatcher.start(m_logger);
-
- // Reload the cached bundles bundles before creating and starting
- // the system bundle, since we want all cached bundles to be reloaded
- // when we activate the system bundle and any subsequent custom
- // framework activators passed into the framework constructor.
- BundleArchive[] archives = null;
-
- // First get cached bundle identifiers.
- try
- {
- archives = m_cache.getArchives();
- }
- catch (Exception ex)
- {
- m_logger.log(
- Logger.LOG_ERROR,
- "Unable to list saved bundles.",
- ex);
- archives = null;
- }
-
- // create the system bundle that is responsible for providing specific
- // container related services.
- IContentLoader cl = m_extensionManager;
- cl.setSearchPolicy(
- new R4SearchPolicy(
- m_policyCore, m_systemBundleInfo.getCurrentModule()));
- m_factory.setContentLoader(
- m_systemBundleInfo.getCurrentModule(),
- cl);
-
- try
- {
- addSecurity(this);
- }
- catch (Exception e)
- {
- // This should not happen
- }
-
- // Note: we need this ordering to launch:
- // 1) create all stuff of the system bundle (extension manager needs it)
- // 2) install all bundles from cache (will start extension bundles)
- // 3) start the system bundle (will start custom activators)
- // 4) start the other bundles via the start level
- // it is important to keep this order because bundles are installed
- // from added activators in step 3 and extension bundles are started
- // in 2 and need the stuff from 1.
- m_installedBundleMap.put(
- m_systemBundleInfo.getLocation(), this);
- m_installedBundleIndex.put(new Long(0), this);
-
- // Create system bundle activator.
- m_systemBundleInfo.setActivator(new SystemBundleActivator());
-
- // Create the bundle context for the system bundle and
- // then activate it.
- m_systemBundleInfo.setBundleContext(
- new BundleContextImpl(m_logger, this, this));
-
- FelixBundle bundle = null;
-
- // Now install all cached bundles.
- for (int i = 0; (archives != null) && (i < archives.length); i++)
- {
+ // Create the bundle cache so that we can reload any installed bundles.
try
{
- // Keep track of the max bundle ID currently in use since we
- // will need to use this as our next bundle ID value if the
- // persisted value cannot be read.
- m_nextId = Math.max(m_nextId, archives[i].getId() + 1);
-
- // It is possible that a bundle in the cache was previously
- // uninstalled, but not completely deleted (perhaps because
- // of a crash or a locked file), so if we see an archive
- // with an UNINSTALLED persistent state, then try to remove
- // it now.
- if (archives[i].getPersistentState() == Bundle.UNINSTALLED)
- {
- m_cache.remove(archives[i]);
- }
- // Otherwise re-install the cached bundle.
- else
- {
- // Install the cached bundle.
- bundle = (FelixBundle) installBundle(
- archives[i].getId(), archives[i].getLocation(), null);
- }
+ m_cache = new BundleCache(m_logger, m_configMap);
}
catch (Exception ex)
{
-ex.printStackTrace();
- fireFrameworkEvent(FrameworkEvent.ERROR, this, ex);
- try
- {
- m_logger.log(
- Logger.LOG_ERROR,
- "Unable to re-install " + archives[i].getLocation(),
- ex);
- }
- catch (Exception ex2)
- {
- m_logger.log(
- Logger.LOG_ERROR,
- "Unable to re-install cached bundle.",
- ex);
- }
- // TODO: FRAMEWORK - Perhaps we should remove the cached bundle?
+ m_logger.log(Logger.LOG_ERROR, "Error creating bundle cache.", ex);
+ throw new BundleException("Error creating bundle cache.", ex);
}
- }
- // Now that we have loaded all cached bundles and have determined the
- // max bundle ID of cached bundles, we need to try to load the next
- // bundle ID from persistent storage. In case of failure, we should
- // keep the max value.
- m_nextId = Math.max(m_nextId, loadNextId());
+ // Initialize installed bundle data structures.
+ m_installedBundleMap = new HashMap();
+ m_installedBundleIndex = new TreeMap();
- // Get the framework's default start level.
- int startLevel = FelixConstants.FRAMEWORK_DEFAULT_STARTLEVEL;
- String s = (String) m_configMap.get(FelixConstants.FRAMEWORK_STARTLEVEL_PROP);
- if (s != null)
- {
- try
- {
- startLevel = Integer.parseInt(s);
- }
- catch (NumberFormatException ex)
- {
- startLevel = FelixConstants.FRAMEWORK_DEFAULT_STARTLEVEL;
- }
- }
+ // Add the system bundle to the set of installed bundles.
+ m_installedBundleMap.put(m_sbi.getLocation(), this);
+ m_installedBundleIndex.put(new Long(0), this);
- // Now that the cached bundles are reloaded,
- // activating all custom framework activators.
- try
- {
- // Manually resolve the System Bundle, which will cause its
+ // Manually resolve the system bundle, which will cause its
// state to be set to RESOLVED.
try
{
- m_policyCore.resolve(m_systemBundleInfo.getCurrentModule());
+ m_policyCore.resolve(m_sbi.getCurrentModule());
}
catch (ResolveException ex)
{
@@ -849,51 +706,185 @@
+ ex.getRequirement());
}
- Felix.m_secureAction.startActivator(m_systemBundleInfo.getActivator(),
- m_systemBundleInfo.getBundleContext());
- }
- catch (Throwable ex)
- {
- m_factory = null;
- EventDispatcher.shutdown();
- m_logger.log(Logger.LOG_ERROR, "Unable to start system bundle.", ex);
- throw new RuntimeException("Unable to start system bundle.");
- }
+ // Reload the cached bundles before creating and starting the
+ // system bundle, since we want all cached bundles to be reloaded
+ // when we activate the system bundle and any subsequent system
+ // bundle activators passed into the framework constructor.
+ BundleArchive[] archives = null;
- // Now that the system bundle is successfully created we can give
- // its bundle context to the logger so that it can track log services.
- m_logger.setSystemBundleContext(m_systemBundleInfo.getBundleContext());
-
- // Set the start level using the start level service;
- // this ensures that all start level requests are
- // serialized.
- try
- {
- StartLevel sl = (StartLevel) getService(
- getBundle(0),getServiceReferences((FelixBundle) getBundle(0),
- StartLevel.class.getName(), null, true)[0]);
- if (sl instanceof StartLevelImpl)
+ // First get cached bundle identifiers.
+ try
{
- ((StartLevelImpl) sl).setStartLevelAndWait(startLevel);
+ archives = m_cache.getArchives();
}
- else
+ catch (Exception ex)
{
- sl.setStartLevel(startLevel);
+ m_logger.log(Logger.LOG_ERROR, "Unable to list saved bundles.", ex);
+ archives = null;
}
+
+ // Now load all cached bundles.
+ for (int i = 0; (archives != null) && (i < archives.length); i++)
+ {
+ try
+ {
+ // Keep track of the max bundle ID currently in use since we
+ // will need to use this as our next bundle ID value if the
+ // persisted value cannot be read.
+ m_nextId = Math.max(m_nextId, archives[i].getId() + 1);
+
+ // It is possible that a bundle in the cache was previously
+ // uninstalled, but not completely deleted (perhaps because
+ // of a crash or a locked file), so if we see an archive
+ // with an UNINSTALLED persistent state, then try to remove
+ // it now.
+ if (archives[i].getPersistentState() == Bundle.UNINSTALLED)
+ {
+ m_cache.remove(archives[i]);
+ }
+ // Otherwise re-install the cached bundle.
+ else
+ {
+ // Install the cached bundle.
+ installBundle(archives[i].getId(), archives[i].getLocation(), null);
+ }
+ }
+ catch (Exception ex)
+ {
+ex.printStackTrace();
+ fireFrameworkEvent(FrameworkEvent.ERROR, this, ex);
+ try
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "Unable to re-install " + archives[i].getLocation(),
+ ex);
+ }
+ catch (Exception ex2)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "Unable to re-install cached bundle.",
+ ex);
+ }
+ // TODO: FRAMEWORK - Perhaps we should remove the cached bundle?
+ }
+ }
+
+ // Now that we have loaded all cached bundles and have determined the
+ // max bundle ID of cached bundles, we need to try to load the next
+ // bundle ID from persistent storage. In case of failure, we should
+ // keep the max value.
+ m_nextId = Math.max(m_nextId, loadNextId());
+
+ // Create service registry.
+ m_registry = new ServiceRegistry(m_logger);
+ // Add a listener to the service registry; this is
+ // used to distribute service registry events to
+ // service listeners.
+ m_registry.addServiceListener(new ServiceListener() {
+ public void serviceChanged(ServiceEvent event)
+ {
+ fireServiceEvent(event);
+ }
+ });
+
+ // The framework is now in its startup sequence.
+ m_sbi.setState(Bundle.STARTING);
+
+ // Now it is possible for threads to wait for the framework to stop,
+ // so create a gate for that purpose.
+ m_shutdownGate = new ThreadGate();
+
+ // Create system bundle activator and bundle context so we can activate it.
+ m_sbi.setActivator(new SystemBundleActivator());
+ m_sbi.setBundleContext(new BundleContextImpl(m_logger, this, this));
+ try
+ {
+ Felix.m_secureAction.startActivator(
+ m_sbi.getActivator(), m_sbi.getBundleContext());
+ }
+ catch (Throwable ex)
+ {
+ m_factory = null;
+ EventDispatcher.shutdown();
+ m_logger.log(Logger.LOG_ERROR, "Unable to start system bundle.", ex);
+ throw new RuntimeException("Unable to start system bundle.");
+ }
+
+ // Now that the system bundle is successfully created we can give
+ // its bundle context to the logger so that it can track log services.
+ m_logger.setSystemBundleContext(m_sbi.getBundleContext());
}
- catch (InvalidSyntaxException ex)
+ }
+
+ /**
+ * This method starts the framework instance, which will transition the
+ * framework from start level 0 to its active start level as specified in
+ * its configuration properties (1 by default). If the <tt>init()</tt> was
+ * not explicitly invoked before calling this method, then it will be
+ * implicitly invoked before starting the framework.
+ *
+ * @throws org.osgi.framework.BundleException if any error occurs.
+ **/
+ public synchronized void start() throws BundleException
+ {
+ final int state = m_sbi.getState();
+ // Initialize if necessary.
+ if ((state == Bundle.INSTALLED) || (state == Bundle.RESOLVED))
{
- // Should never happen.
+ init();
}
- // The framework is now running.
- m_systemBundleInfo.setState(Bundle.ACTIVE);
+ // If the current state is STARTING, then the system bundle can be started.
+ if (m_sbi.getState() == Bundle.STARTING)
+ {
+ // Get the framework's default start level.
+ int startLevel = FelixConstants.FRAMEWORK_DEFAULT_STARTLEVEL;
+ String s = (String) m_configMap.get(FelixConstants.FRAMEWORK_STARTLEVEL_PROP);
+ if (s != null)
+ {
+ try
+ {
+ startLevel = Integer.parseInt(s);
+ }
+ catch (NumberFormatException ex)
+ {
+ startLevel = FelixConstants.FRAMEWORK_DEFAULT_STARTLEVEL;
+ }
+ }
- // Fire started event for system bundle.
- fireBundleEvent(BundleEvent.STARTED, this);
+ // Set the start level using the start level service;
+ // this ensures that all start level requests are
+ // serialized.
+ try
+ {
+ StartLevel sl = (StartLevel) getService(
+ getBundle(0),getServiceReferences((FelixBundle) getBundle(0),
+ StartLevel.class.getName(), null, true)[0]);
+ if (sl instanceof StartLevelImpl)
+ {
+ ((StartLevelImpl) sl).setStartLevelAndWait(startLevel);
+ }
+ else
+ {
+ sl.setStartLevel(startLevel);
+ }
+ }
+ catch (InvalidSyntaxException ex)
+ {
+ // Should never happen.
+ }
- // Send a framework event to indicate the framework has started.
- fireFrameworkEvent(FrameworkEvent.STARTED, this, null);
+ // The framework is now running.
+ m_sbi.setState(Bundle.ACTIVE);
+
+ // Fire started event for system bundle.
+ fireBundleEvent(BundleEvent.STARTED, this);
+
+ // Send a framework event to indicate the framework has started.
+ fireFrameworkEvent(FrameworkEvent.STARTED, this, null);
+ }
}
public void start(int options) throws BundleException
@@ -904,7 +895,7 @@
}
/**
- * This method cleanly shuts down the framework, it must be called at the
+ * This method asynchronously shuts down the framework, it must be called at the
* end of a session in order to shutdown all active bundles.
**/
public void stop() throws BundleException
@@ -927,41 +918,24 @@
stop();
}
- public void stopAndWait()
+ /**
+ * This method will cause the calling thread to block until the framework
+ * shuts down.
+ * @param timeout A timeout value.
+ * @throws java.lang.InterruptedException If the thread was interrupted.
+ **/
+ public void waitForStop(long timeout) throws InterruptedException
{
- // Shut the framework down by calling stop() on the system bundle.
- // Since stop() on the system bundle will return immediately, we
- // will synchronize on the framework instance so we can use it to
- // be notified when the shutdown is complete.
+ // If there is a gate, wait on it; otherwise, return immediately.
+ ThreadGate gate;
synchronized (this)
{
- if (m_systemBundleInfo.getState() == Bundle.ACTIVE)
- {
- try
- {
- getBundle(0).stop();
- }
- catch (BundleException ex)
- {
- fireFrameworkEvent(FrameworkEvent.ERROR, this, ex);
- m_logger.log(
- Logger.LOG_ERROR,
- "Error stopping system bundle.",
- ex);
- }
- }
+ gate = m_shutdownGate;
+ }
- while (m_systemBundleInfo.getState() != Bundle.UNINSTALLED)
- {
- try
- {
- wait();
- }
- catch (InterruptedException ex)
- {
- // Keep waiting if necessary.
- }
- }
+ if (gate != null)
+ {
+ gate.await(timeout);
}
}
@@ -1176,7 +1150,7 @@
bundles[i] = null;
}
- if (m_systemBundleInfo.getState() == Bundle.ACTIVE)
+ if (m_sbi.getState() == Bundle.ACTIVE)
{
fireFrameworkEvent(FrameworkEvent.STARTLEVEL_CHANGED, this, null);
}
@@ -1845,7 +1819,7 @@
{
addSecurity(bundle);
m_extensionManager.addExtensionBundle(this, bundle);
- m_factory.refreshModule(m_systemBundleInfo.getCurrentModule());
+ m_factory.refreshModule(m_sbi.getCurrentModule());
bundle.getInfo().setState(Bundle.RESOLVED);
}
else if (bundle.getInfo().isExtension())
@@ -2013,25 +1987,6 @@
{
m_secureAction.stopActivator(info.getActivator(), info.getBundleContext());
}
-
- // Try to save the activator in the cache.
- // NOTE: This is non-standard OSGi behavior and only
- // occurs if strictness is disabled.
- String strict = (String) m_configMap.get(FelixConstants.STRICT_OSGI_PROP);
- boolean isStrict = (strict == null) ? true : strict.equals("true");
- if (!isStrict)
- {
- try
- {
- m_cache.getArchive(info.getBundleId())
- .setActivator(info.getActivator());
- }
- catch (Exception ex)
- {
- // Problem saving activator, so ignore it.
- // TODO: FRAMEWORK - Perhaps we should handle this some other way?
- }
- }
}
catch (Throwable th)
{
@@ -2326,7 +2281,7 @@
else
{
m_extensionManager.addExtensionBundle(this, bundle);
- m_factory.refreshModule(m_systemBundleInfo.getCurrentModule());
+ m_factory.refreshModule(m_sbi.getCurrentModule());
}
}
@@ -2847,11 +2802,10 @@
{
if (bundle == this)
{
- return m_systemBundleInfo.getArchive().getDataFile(s);
+ return m_cache.getSystemBundleDataFile(s);
}
- return m_cache.getArchive(
- bundle.getBundleId()).getDataFile(s);
+ return m_cache.getArchive(bundle.getBundleId()).getDataFile(s);
}
catch (Exception ex)
{
@@ -2891,11 +2845,11 @@
}
}
}
- try
+ try
{
return (getInfo().getCurrentModule().getClass(clazz.getName()) == clazz) ? this : null;
}
- catch(ClassNotFoundException ex)
+ catch(ClassNotFoundException ex)
{
return null;
}
@@ -3301,7 +3255,7 @@
// Miscellaneous private methods.
//
- private BundleInfo createBundleInfo(BundleArchive archive, Map headerMap, boolean isExtension)
+ private RegularBundleInfo createBundleInfo(BundleArchive archive, Map headerMap, boolean isExtension)
throws Exception
{
// Create the module for the bundle; although there should only
@@ -3312,7 +3266,7 @@
isExtension);
// Finally, create an return the bundle info.
- BundleInfo info = new BundleInfo(m_logger, archive, module);
+ RegularBundleInfo info = new RegularBundleInfo(m_logger, archive, module);
info.setExtension(isExtension);
return info;
@@ -3458,45 +3412,24 @@
// This method is called indirectly from startBundle() (via _startBundle()),
// which has the exclusion lock, so there is no need to do any locking here.
+ // Get the activator class from the header map.
BundleActivator activator = null;
-
- String strict = (String) m_configMap.get(FelixConstants.STRICT_OSGI_PROP);
- boolean isStrict = (strict == null) ? true : strict.equals("true");
- if (!isStrict)
+ Map headerMap = info.getCurrentHeader();
+ String className = (String) headerMap.get(Constants.BUNDLE_ACTIVATOR);
+ // Try to instantiate activator class if present.
+ if (className != null)
{
+ className = className.trim();
+ Class clazz;
try
{
- activator = info.getArchive().getActivator(info.getCurrentModule());
+ clazz = info.getCurrentModule().getClass(className);
}
- catch (Exception ex)
- {
- activator = null;
+ catch (ClassNotFoundException ex) {
+ throw new BundleException("Not found: "
+ + className, ex);
}
- }
-
- // If there was no cached activator, then get the activator
- // class from the bundle manifest.
- if (activator == null)
- {
- // Get the activator class from the header map.
- Map headerMap = info.getCurrentHeader();
- String className = (String) headerMap.get(Constants.BUNDLE_ACTIVATOR);
- // Try to instantiate activator class if present.
- if (className != null)
- {
- className = className.trim();
- Class clazz;
- try
- {
- clazz = info.getCurrentModule().getClass(className);
- }
- catch (ClassNotFoundException ex) {
- throw new BundleException("Not found: "
- + className, ex);
- }
-
- activator = (BundleActivator) clazz.newInstance();
- }
+ activator = (BundleActivator) clazz.newInstance();
}
return activator;
@@ -3799,7 +3732,7 @@
{
// Change framework state from active to stopping.
// If framework is not active, then just return.
- if (m_systemBundleInfo.getState() != Bundle.STOPPING)
+ if (m_sbi.getState() != Bundle.STOPPING)
{
return;
}
@@ -3832,7 +3765,9 @@
for (int i = 0; i < bundles.length; i++)
{
FelixBundle bundle = (FelixBundle) bundles[i];
- if (bundle.getInfo().getArchive().getRevisionCount() > 1)
+ BundleInfo info = bundle.getInfo();
+ if ((info instanceof RegularBundleInfo) &&
+ (((RegularBundleInfo) info).getArchive().getRevisionCount() > 1))
{
try
{
@@ -3911,19 +3846,13 @@
m_extensionManager.removeExtensions(Felix.this);
}
- // Notify any waiters that the framework is back in its initial state.
+ // Set the framework state to resolved.
synchronized (Felix.this)
{
- m_systemBundleInfo.setState(Bundle.UNINSTALLED);
- Felix.this.notifyAll();
- }
-
- // Finally shutdown the JVM if the framework is running stand-alone.
- String embedded = (String) m_configMap.get(FelixConstants.EMBEDDED_EXECUTION_PROP);
- boolean isEmbedded = (embedded == null) ? false : embedded.equals("true");
- if (!isEmbedded)
- {
- m_secureAction.exit(0);
+ m_sbi.setState(Bundle.RESOLVED);
+ m_shutdownGate.open();
+ m_shutdownGate = null;
+ m_shutdownThread = null;
}
}
}
@@ -3997,8 +3926,8 @@
{
try
{
- BundleInfo info = m_bundle.getInfo();
- BundleInfo newInfo = createBundleInfo(
+ RegularBundleInfo info = (RegularBundleInfo) m_bundle.getInfo();
+ RegularBundleInfo newInfo = createBundleInfo(
info.getArchive(), info.getCurrentHeader(), info.isExtension());
newInfo.syncLock(info);
((BundleImpl) m_bundle).setInfo(newInfo);
@@ -4300,7 +4229,9 @@
while (iter.hasNext())
{
FelixBundle bundle = (FelixBundle) iter.next();
- if (bundle.getInfo().getArchive().getRevisionCount() > 1)
+ BundleInfo info = bundle.getInfo();
+ if ((info instanceof RegularBundleInfo) &&
+ (((RegularBundleInfo) info).getArchive().getRevisionCount() > 1))
{
list.add(bundle);
}
diff --git a/framework/src/main/java/org/apache/felix/framework/FelixBundle.java b/framework/src/main/java/org/apache/felix/framework/FelixBundle.java
index 7810548..4e27160 100644
--- a/framework/src/main/java/org/apache/felix/framework/FelixBundle.java
+++ b/framework/src/main/java/org/apache/felix/framework/FelixBundle.java
@@ -23,4 +23,4 @@
abstract class FelixBundle implements Bundle
{
/* package private */ abstract BundleInfo getInfo();
-}
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/RegularBundleInfo.java b/framework/src/main/java/org/apache/felix/framework/RegularBundleInfo.java
new file mode 100644
index 0000000..8a781fd
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/RegularBundleInfo.java
@@ -0,0 +1,580 @@
+/*
+ * 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.felix.framework;
+
+import java.io.IOException;
+import java.net.URL;
+import java.security.ProtectionDomain;
+import java.util.*;
+
+import org.apache.felix.framework.cache.BundleArchive;
+import org.apache.felix.framework.searchpolicy.ModuleDefinition;
+import org.apache.felix.framework.util.manifestparser.ManifestParser;
+import org.apache.felix.moduleloader.ICapability;
+import org.apache.felix.moduleloader.IContentLoader;
+import org.apache.felix.moduleloader.IModule;
+import org.osgi.framework.*;
+
+class RegularBundleInfo extends BundleInfo
+{
+ private Logger m_logger = null;
+ private BundleArchive m_archive = null;
+ private IModule[] m_modules = null;
+ private int m_state = 0;
+ private BundleActivator m_activator = null;
+ private BundleContext m_context = null;
+ private String m_cachedSymbolicName = null;
+ private long m_cachedSymbolicNameTimestamp;
+ private Map m_cachedHeaders = new HashMap();
+ private long m_cachedHeadersTimestamp;
+
+ // Indicates whether the bundle is stale, meaning that it has
+ // been refreshed and completely removed from the framework.
+ private boolean m_stale = false;
+
+ // Indicates whether the bundle is an extension, meaning that it is
+ // installed as an extension bundle to the framework (i.e., can not be
+ // removed or updated until a framework restart.
+ private boolean m_extension = false;
+
+ // Used for bundle locking.
+ private int m_lockCount = 0;
+ private Thread m_lockThread = null;
+
+ protected RegularBundleInfo(Logger logger, BundleArchive archive, IModule module)
+ {
+ m_logger = logger;
+ m_archive = archive;
+ m_modules = (module == null) ? new IModule[0] : new IModule[] { module };
+
+ m_state = Bundle.INSTALLED;
+ m_stale = false;
+ m_activator = null;
+ m_context = null;
+ }
+
+ /**
+ * Returns the bundle archive associated with this bundle.
+ * @return the bundle archive associated with this bundle.
+ **/
+// TODO: KARL - Should this be on BundleInfo and just return in SystemBundleInfo or exception?
+ public BundleArchive getArchive()
+ {
+ return m_archive;
+ }
+
+ /**
+ * Returns an array of all modules associated with the bundle represented by
+ * this <tt>BundleInfo</tt> object. A module in the array corresponds to a
+ * revision of the bundle's JAR file and is ordered from oldest to newest.
+ * Multiple revisions of a bundle JAR file might exist if a bundle is
+ * updated, without refreshing the framework. In this case, exports from
+ * the prior revisions of the bundle JAR file are still offered; the
+ * current revision will be bound to packages from the prior revision,
+ * unless the packages were not offered by the prior revision. There is
+ * no limit on the potential number of bundle JAR file revisions.
+ * @return array of modules corresponding to the bundle JAR file revisions.
+ **/
+ public synchronized IModule[] getModules()
+ {
+ return m_modules;
+ }
+
+ /**
+ * Determines if the specified module is associated with this bundle.
+ * @param module the module to determine if it is associate with this bundle.
+ * @return <tt>true</tt> if the specified module is in the array of modules
+ * associated with this bundle, <tt>false</tt> otherwise.
+ **/
+ public synchronized boolean hasModule(IModule module)
+ {
+ for (int i = 0; i < m_modules.length; i++)
+ {
+ if (m_modules[i] == module)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the newest module, which corresponds to the last module
+ * in the module array.
+ * @return the newest module.
+ **/
+ public synchronized IModule getCurrentModule()
+ {
+ return m_modules[m_modules.length - 1];
+ }
+
+ /**
+ * Add a module that corresponds to a new bundle JAR file revision for
+ * the bundle associated with this <tt>BundleInfo</tt> object.
+ * @param module the module to add.
+ **/
+ public synchronized void addModule(IModule module)
+ {
+ IModule[] dest = new IModule[m_modules.length + 1];
+ System.arraycopy(m_modules, 0, dest, 0, m_modules.length);
+ dest[m_modules.length] = module;
+ m_modules = dest;
+ }
+
+ public synchronized String getSymbolicName()
+ {
+ // If the bundle has been updated, clear the cached symbolic name.
+ if (getLastModified() > m_cachedSymbolicNameTimestamp)
+ {
+ m_cachedSymbolicName = null;
+ m_cachedSymbolicNameTimestamp = getLastModified();
+ try
+ {
+ // TODO: FRAMEWORK - Rather than reparsing every time, I wonder if
+ // we should be caching this value some place.
+ final ICapability moduleCap = ManifestParser.parseBundleSymbolicName(getCurrentHeader());
+ if (moduleCap != null)
+ {
+ m_cachedSymbolicName = (String) moduleCap.getProperties().get(Constants.BUNDLE_SYMBOLICNAME_ATTRIBUTE);
+ }
+ }
+ catch (BundleException ex)
+ {
+ // Return null.
+ }
+ }
+ return m_cachedSymbolicName;
+ }
+
+ public long getBundleId()
+ {
+ try
+ {
+ return m_archive.getId();
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "Error getting the identifier from bundle archive.",
+ ex);
+ return -1;
+ }
+ }
+
+ public String getLocation()
+ {
+ try
+ {
+ return m_archive.getLocation();
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "Error getting location from bundle archive.",
+ ex);
+ return null;
+ }
+ }
+
+ public int getStartLevel(int defaultLevel)
+ {
+ try
+ {
+ return m_archive.getStartLevel();
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "Error reading start level from bundle archive.",
+ ex);
+ return defaultLevel;
+ }
+ }
+
+ public void setStartLevel(int i)
+ {
+ try
+ {
+ m_archive.setStartLevel(i);
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "Error writing start level to bundle archive.",
+ ex);
+ }
+ }
+
+ public Map getCurrentHeader()
+ {
+ Map headerMap = null;
+ // Special case the system bundle
+ if (getBundleId() == 0)
+ {
+ // TODO: REFACTOR - This is sort of a hack, we should just expose
+ // the bundle symbolic name from our API.
+ try
+ {
+ headerMap = m_archive.getRevision(0).getManifestHeader();
+ }
+ catch (Exception ex)
+ {
+ // This should never happen.
+ }
+ }
+ else
+ {
+ headerMap = ((ModuleDefinition) getCurrentModule().getDefinition()).getHeaders();
+ }
+
+ return headerMap;
+ }
+
+ public Map getCurrentLocalizedHeader(String locale)
+ {
+ synchronized (m_cachedHeaders)
+ {
+ // If the bundle has been updated, clear the cached headers
+ if (getLastModified() > m_cachedHeadersTimestamp)
+ {
+ m_cachedHeaders.clear();
+ }
+ else
+ {
+ // Check if headers for this locale have already been resolved
+ if (m_cachedHeaders.containsKey(locale))
+ {
+ return (Map) m_cachedHeaders.get(locale);
+ }
+ }
+ }
+
+ Map rawHeaders = getCurrentHeader();
+ Map headers = new HashMap(rawHeaders.size());
+ headers.putAll(rawHeaders);
+
+ // Check to see if we actually need to localize anything
+ boolean needsLocalization = false;
+ for (Iterator it = headers.values().iterator(); it.hasNext(); )
+ {
+ if (((String) it.next()).startsWith("%"))
+ {
+ needsLocalization = true;
+ break;
+ }
+ }
+
+ if (!needsLocalization)
+ {
+ // If localization is not needed, just cache the headers and return them as-is
+ // Not sure if this is useful
+ updateHeaderCache(locale, headers);
+ return headers;
+ }
+
+ // Do localization here and return the localized headers
+ IContentLoader loader = this.getCurrentModule().getContentLoader();
+
+ String basename = (String) headers.get(Constants.BUNDLE_LOCALIZATION);
+ if (basename == null)
+ {
+ basename = Constants.BUNDLE_LOCALIZATION_DEFAULT_BASENAME;
+ }
+
+ // Create ordered list of files to load properties from
+ List resourceList = createResourceList(basename, locale);
+
+ // Create a merged props file with all available props for this locale
+ Properties mergedProperties = new Properties();
+ for (Iterator it = resourceList.iterator(); it.hasNext(); )
+ {
+ URL temp = loader.getResource(it.next() + ".properties");
+ if (temp == null)
+ {
+ continue;
+ }
+ try
+ {
+ mergedProperties.load(temp.openConnection().getInputStream());
+ }
+ catch (IOException ex)
+ {
+ // File doesn't exist, just continue loop
+ }
+ }
+
+ // Resolve all localized header entries
+ for (Iterator it = headers.entrySet().iterator(); it.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) it.next();
+ String value = (String) entry.getValue();
+ if (value.startsWith("%"))
+ {
+ String newvalue;
+ String key = value.substring(value.indexOf("%") + 1);
+ newvalue = mergedProperties.getProperty(key);
+ if (newvalue==null)
+ {
+ newvalue = key;
+ }
+ entry.setValue(newvalue);
+ }
+ }
+
+ updateHeaderCache(locale, headers);
+ return headers;
+ }
+
+ private void updateHeaderCache(String locale, Map localizedHeaders)
+ {
+ synchronized (m_cachedHeaders)
+ {
+ m_cachedHeaders.put(locale, localizedHeaders);
+ m_cachedHeadersTimestamp = System.currentTimeMillis();
+ }
+ }
+
+ private List createResourceList(String basename, String locale)
+ {
+ List result = new ArrayList(4);
+
+ StringTokenizer tokens;
+ StringBuffer tempLocale = new StringBuffer(basename);
+
+ result.add(tempLocale.toString());
+
+ if (locale.length() > 0)
+ {
+ tokens = new StringTokenizer(locale, "_");
+ while (tokens.hasMoreTokens())
+ {
+ tempLocale.append("_").append(tokens.nextToken());
+ result.add(tempLocale.toString());
+ }
+ }
+ return result;
+ }
+
+ public synchronized int getState()
+ {
+ return m_state;
+ }
+
+ public synchronized void setState(int i)
+ {
+ m_state = i;
+ }
+
+ public long getLastModified()
+ {
+ try
+ {
+ return m_archive.getLastModified();
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "Error reading last modification time from bundle archive.",
+ ex);
+ return 0;
+ }
+ }
+
+ public void setLastModified(long l)
+ {
+ try
+ {
+ m_archive.setLastModified(l);
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "Error writing last modification time to bundle archive.",
+ ex);
+ }
+ }
+
+ public int getPersistentState()
+ {
+ try
+ {
+ return m_archive.getPersistentState();
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "Error reading persistent state from bundle archive.",
+ ex);
+ return Bundle.INSTALLED;
+ }
+ }
+
+ public void setPersistentStateInactive()
+ {
+ try
+ {
+ m_archive.setPersistentState(Bundle.INSTALLED);
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(Logger.LOG_ERROR,
+ "Error writing persistent state to bundle archive.",
+ ex);
+ }
+ }
+
+ public void setPersistentStateActive()
+ {
+ try
+ {
+ m_archive.setPersistentState(Bundle.ACTIVE);
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "Error writing persistent state to bundle archive.",
+ ex);
+ }
+ }
+
+ public void setPersistentStateUninstalled()
+ {
+ try
+ {
+ m_archive.setPersistentState(Bundle.UNINSTALLED);
+ }
+ catch (Exception ex)
+ {
+ m_logger.log(
+ Logger.LOG_ERROR,
+ "Error writing persistent state to bundle archive.",
+ ex);
+ }
+ }
+
+ public synchronized BundleContext getBundleContext()
+ {
+ return m_context;
+ }
+
+ public synchronized void setBundleContext(BundleContext context)
+ {
+ m_context = context;
+ }
+
+ public synchronized BundleActivator getActivator()
+ {
+ return m_activator;
+ }
+
+ public synchronized void setActivator(BundleActivator activator)
+ {
+ m_activator = activator;
+ }
+
+ public synchronized boolean isStale()
+ {
+ return m_stale;
+ }
+
+ public synchronized void setStale()
+ {
+ m_stale = true;
+ }
+
+ public synchronized boolean isExtension()
+ {
+ return m_extension;
+ }
+
+ public synchronized void setExtension(boolean extension)
+ {
+ m_extension = extension;
+ }
+
+ //
+ // Locking related methods.
+ // NOTE: These methods are not synchronized because it is assumed they
+ // will only ever be called when the caller is in a synchronized block.
+ //
+
+ public synchronized boolean isLockable()
+ {
+ return (m_lockCount == 0) || (m_lockThread == Thread.currentThread());
+ }
+
+ public synchronized void lock()
+ {
+ if ((m_lockCount > 0) && (m_lockThread != Thread.currentThread()))
+ {
+ throw new IllegalStateException("Bundle is locked by another thread.");
+ }
+ m_lockCount++;
+ m_lockThread = Thread.currentThread();
+ }
+
+ public synchronized void unlock()
+ {
+ if (m_lockCount == 0)
+ {
+ throw new IllegalStateException("Bundle is not locked.");
+ }
+ if ((m_lockCount > 0) && (m_lockThread != Thread.currentThread()))
+ {
+ throw new IllegalStateException("Bundle is locked by another thread.");
+ }
+ m_lockCount--;
+ if (m_lockCount == 0)
+ {
+ m_lockThread = null;
+ }
+ }
+
+ public synchronized void syncLock(RegularBundleInfo info)
+ {
+ m_lockCount = info.m_lockCount;
+ m_lockThread = info.m_lockThread;
+ }
+
+ public synchronized void setProtectionDomain(ProtectionDomain pd)
+ {
+ getCurrentModule().getContentLoader().setSecurityContext(pd);
+ }
+
+ public synchronized ProtectionDomain getProtectionDomain()
+ {
+ ProtectionDomain pd = null;
+
+ for (int i = m_modules.length - 1; (i >= 0) && (pd == null); i--)
+ {
+ pd = (ProtectionDomain)
+ m_modules[i].getContentLoader().getSecurityContext();
+ }
+
+ return pd;
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/SystemBundleArchive.java b/framework/src/main/java/org/apache/felix/framework/SystemBundleArchive.java
deleted file mode 100644
index da64e15..0000000
--- a/framework/src/main/java/org/apache/felix/framework/SystemBundleArchive.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * 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.felix.framework;
-
-import org.apache.felix.framework.cache.*;
-import java.io.File;
-import java.io.InputStream;
-import java.util.Map;
-
-import org.apache.felix.framework.util.FelixConstants;
-import org.apache.felix.framework.util.StringMap;
-import org.apache.felix.moduleloader.IContent;
-import org.apache.felix.moduleloader.IModule;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleActivator;
-
-/**
- * <p>
- * This class represents the bundle archive of the system bundle. It is a
- * special case that is mostly just an empty implementation, since the system
- * bundle is not a real archive.
- * </p>
-**/
-public class SystemBundleArchive extends BundleArchive
-{
- private BundleCache m_cache;
- private Map m_headerMap = new StringMap(false);
- private BundleRevision m_revision;
-
- public SystemBundleArchive(BundleCache cache)
- {
- m_cache = cache;
-
- try
- {
- m_revision = new BundleRevision(null, null, null) {
-
- public Map getManifestHeader() throws Exception
- {
- return m_headerMap;
- }
-
- public IContent getContent() throws Exception
- {
- return null;
- }
-
- public void dispose() throws Exception
- {
- }
- };
- }
- catch (Exception ex)
- {
- // This should never happen.
- }
- }
-
- public long getId()
- {
- return 0;
- }
-
- public String getLocation() throws Exception
- {
- return FelixConstants.SYSTEM_BUNDLE_LOCATION;
- }
-
- public String getCurrentLocation() throws Exception
- {
- return null;
- }
-
- public void setCurrentLocation(String location) throws Exception
- {
- }
-
- public int getPersistentState() throws Exception
- {
- return Bundle.ACTIVE;
- }
-
- public void setPersistentState(int state) throws Exception
- {
- }
-
- public int getStartLevel() throws Exception
- {
- return FelixConstants.SYSTEMBUNDLE_DEFAULT_STARTLEVEL;
- }
-
- public void setStartLevel(int level) throws Exception
- {
- }
-
- public File getDataFile(String fileName) throws Exception
- {
- return m_cache.getSystemBundleDataFile(fileName);
- }
-
- public BundleActivator getActivator(IModule module)
- throws Exception
- {
- return null;
- }
-
- public void setActivator(Object obj) throws Exception
- {
- }
-
- public int getRevisionCount()
- {
- return 1;
- }
-
- public BundleRevision getRevision(int i)
- {
- return m_revision;
- }
-
- public void revise(String location, InputStream is)
- throws Exception
- {
- }
-
- public void purge() throws Exception
- {
- }
-
- public void dispose() throws Exception
- {
- }
-
- public Map getManifestHeader(int revision)
- {
- return m_headerMap;
- }
-}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/SystemBundleInfo.java b/framework/src/main/java/org/apache/felix/framework/SystemBundleInfo.java
new file mode 100644
index 0000000..e2177e6
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/SystemBundleInfo.java
@@ -0,0 +1,90 @@
+/*
+ * 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.felix.framework;
+
+import java.util.Map;
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.StringMap;
+import org.osgi.framework.Bundle;
+
+class SystemBundleInfo extends BundleInfo
+{
+ private Map m_headerMap = new StringMap(false);
+ private int m_startLevel = FelixConstants.SYSTEMBUNDLE_DEFAULT_STARTLEVEL;
+
+ public String getSymbolicName()
+ {
+ return FelixConstants.SYSTEM_BUNDLE_SYMBOLICNAME;
+ }
+
+ public long getBundleId()
+ {
+ return 0;
+ }
+
+ public String getLocation()
+ {
+ return FelixConstants.SYSTEM_BUNDLE_LOCATION;
+ }
+
+ public synchronized int getStartLevel(int defaultLevel)
+ {
+ return m_startLevel;
+ }
+
+ public synchronized void setStartLevel(int i)
+ {
+ m_startLevel = i;
+ }
+
+ public Map getCurrentHeader()
+ {
+ return m_headerMap;
+ }
+
+ public long getLastModified()
+ {
+ return 0;
+ }
+
+ public void setLastModified(long l)
+ {
+ // Ignore.
+ }
+
+ public int getPersistentState()
+ {
+ return Bundle.ACTIVE;
+ }
+
+ public void setPersistentStateInactive()
+ {
+ // Ignore.
+ }
+
+ public void setPersistentStateActive()
+ {
+ // Ignore.
+ }
+
+ public void setPersistentStateUninstalled()
+ {
+ // Ignore.
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java b/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
index 43692ca..525dad3 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/BundleCache.java
@@ -23,6 +23,7 @@
import org.apache.felix.framework.Logger;
import org.apache.felix.framework.util.SecureAction;
+import org.osgi.framework.SystemBundle;
/**
* <p>
@@ -301,6 +302,9 @@
// See if the profile directory is specified.
String profileDirStr = (String) m_configMap.get(CACHE_PROFILE_DIR_PROP);
+ profileDirStr = (profileDirStr == null)
+ ? (String) m_configMap.get(SystemBundle.FRAMEWORK_STORAGE_PROP)
+ : profileDirStr;
if (profileDirStr != null)
{
m_profileDir = new File(profileDirStr);
diff --git a/framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java b/framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java
index 5e86ac7..f3f9e20 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/FelixConstants.java
@@ -43,8 +43,9 @@
// Miscellaneous framework configuration property names.
public static final String LOG_LEVEL_PROP = "felix.log.level";
- public static final String EMBEDDED_EXECUTION_PROP = "felix.embedded.execution";
- public static final String STRICT_OSGI_PROP = "felix.strict.osgi";
+ public static final String LOG_LOGGER_PROP = "felix.log.logger";
+ public static final String SYSTEMBUNDLE_ACTIVATORS_PROP
+ = "felix.systembundle.activators";
public static final String FRAMEWORK_STARTLEVEL_PROP
= "felix.startlevel.framework";
public static final String BUNDLE_STARTLEVEL_PROP
diff --git a/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java b/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
index 910c118..f497cb8 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
@@ -20,8 +20,6 @@
import java.io.*;
import java.lang.reflect.*;
-import java.net.JarURLConnection;
-import java.net.MalformedURLException;
import java.net.*;
import java.security.*;
import java.util.Hashtable;
@@ -614,28 +612,6 @@
}
}
- public void exit(int code)
- {
- if (System.getSecurityManager() != null)
- {
- try
- {
- Actions actions = (Actions) m_actions.get();
- actions.set(Actions.SYSTEM_EXIT_ACTION, new Integer(code));
- AccessController.doPrivileged(actions, m_acc);
- }
- catch (PrivilegedActionException ex)
- {
- // We don't need to rethrow anything since System.exit throws
- // runtime exceptions only
- }
- }
- else
- {
- System.exit(code);
- }
- }
-
public Policy getPolicy()
{
if (System.getSecurityManager() != null)
@@ -657,22 +633,22 @@
}
}
- public void addURLToURLClassLoader(URL extension, ClassLoader loader) throws Exception
+ public void addURLToURLClassLoader(URL extension, ClassLoader loader) throws Exception
{
- if (System.getSecurityManager() != null)
+ if (System.getSecurityManager() != null)
{
Actions actions = (Actions) m_actions.get();
actions.set(Actions.ADD_EXTENSION_URL, extension, loader);
- try
+ try
{
AccessController.doPrivileged(actions, m_acc);
- }
- catch (PrivilegedActionException e)
+ }
+ catch (PrivilegedActionException e)
{
throw e.getException();
}
}
- else
+ else
{
Method addURL =
URLClassLoader.class.getDeclaredMethod("addURL",
@@ -681,7 +657,7 @@
addURL.invoke(loader, new Object[]{extension});
}
}
-
+
public Constructor getConstructor(Class target, Class[] types) throws Exception
{
if (System.getSecurityManager() != null)
@@ -702,7 +678,7 @@
return target.getConstructor(types);
}
}
-
+
public Method getMethod(Class target, String method, Class[] types) throws Exception
{
if (System.getSecurityManager() != null)
@@ -723,7 +699,7 @@
return target.getMethod(method, types);
}
}
-
+
public Method getDeclaredMethod(Class target, String method, Class[] types) throws Exception
{
if (System.getSecurityManager() != null)
@@ -744,7 +720,7 @@
return target.getDeclaredMethod(method, types);
}
}
-
+
public Object invoke(Method method, Object target, Object[] params) throws Exception
{
if (System.getSecurityManager() != null)
@@ -766,7 +742,7 @@
return method.invoke(target, params);
}
}
-
+
public Object invoke(Constructor constructor, Object[] params) throws Exception
{
if (System.getSecurityManager() != null)
@@ -789,7 +765,7 @@
}
}
- public Object getDeclaredField(Class targetClass, String name, Object target)
+ public Object getDeclaredField(Class targetClass, String name, Object target)
throws Exception
{
if (System.getSecurityManager() != null)
@@ -809,18 +785,18 @@
{
Field field = targetClass.getDeclaredField(name);
field.setAccessible(true);
-
+
return field.get(target);
}
}
- public Object swapStaticFieldIfNotClass(Class targetClazz,
+ public Object swapStaticFieldIfNotClass(Class targetClazz,
Class targetType, Class condition, String lockName) throws Exception
{
if (System.getSecurityManager() != null)
{
Actions actions = (Actions) m_actions.get();
- actions.set(Actions.SWAP_FIELD_ACTION, targetClazz, targetType,
+ actions.set(Actions.SWAP_FIELD_ACTION, targetClazz, targetType,
condition, lockName);
try
{
@@ -833,25 +809,25 @@
}
else
{
- return _swapStaticFieldIfNotClass(targetClazz, targetType,
+ return _swapStaticFieldIfNotClass(targetClazz, targetType,
condition, lockName);
}
}
-
- private static Object _swapStaticFieldIfNotClass(Class targetClazz,
+
+ private static Object _swapStaticFieldIfNotClass(Class targetClazz,
Class targetType, Class condition, String lockName) throws Exception
{
Object lock = null;
if (lockName != null)
{
- try
+ try
{
- Field lockField =
- targetClazz.getDeclaredField(lockName);
+ Field lockField =
+ targetClazz.getDeclaredField(lockName);
lockField.setAccessible(true);
lock = lockField.get(null);
- }
- catch (NoSuchFieldException ex)
+ }
+ catch (NoSuchFieldException ex)
{
}
}
@@ -866,13 +842,13 @@
Object result = null;
for (int i = 0; (i < fields.length) && (result == null); i++)
{
- if (Modifier.isStatic(fields[i].getModifiers()) &&
+ if (Modifier.isStatic(fields[i].getModifiers()) &&
(fields[i].getType() == targetType))
{
fields[i].setAccessible(true);
-
+
result = fields[i].get(null);
-
+
if (result != null)
{
if ((condition == null) ||
@@ -890,7 +866,7 @@
// reset cache
for (int i = 0; i < fields.length; i++)
{
- if (Modifier.isStatic(fields[i].getModifiers()) &&
+ if (Modifier.isStatic(fields[i].getModifiers()) &&
(fields[i].getType() == Hashtable.class))
{
fields[i].setAccessible(true);
@@ -907,7 +883,7 @@
}
return null;
}
-
+
private static class Actions implements PrivilegedExceptionAction
{
public static final int GET_PROPERTY_ACTION = 0;
@@ -1114,7 +1090,7 @@
{
return ((JarURLConnection) m_arg1).getJarFile();
}
- else if (m_action == ADD_EXTENSION_URL)
+ else if (m_action == ADD_EXTENSION_URL)
{
Method addURL =
URLClassLoader.class.getDeclaredMethod("addURL",
@@ -1142,7 +1118,7 @@
}
else if (m_action == SWAP_FIELD_ACTION)
{
- return _swapStaticFieldIfNotClass((Class) m_arg1,
+ return _swapStaticFieldIfNotClass((Class) m_arg1,
(Class) m_arg2, (Class) m_arg3, (String) m_arg4);
}
else if (m_action == GET_FIELD_ACTION)
@@ -1155,7 +1131,7 @@
{
return ((Class) m_arg1).getDeclaredMethod((String) m_arg2, (Class[]) m_arg3);
}
-
+
return null;
}
finally
@@ -1164,4 +1140,4 @@
}
}
}
-}
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/apache/felix/framework/util/ThreadGate.java b/framework/src/main/java/org/apache/felix/framework/util/ThreadGate.java
new file mode 100644
index 0000000..9546f7a
--- /dev/null
+++ b/framework/src/main/java/org/apache/felix/framework/util/ThreadGate.java
@@ -0,0 +1,51 @@
+/*
+ * 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.felix.framework.util;
+
+/**
+ * This class implements a simple one-shot gate for threads. The gate
+ * starts closed and will block any threads that try to wait on it. Once
+ * opened, all waiting threads will be released. The gate cannot be reused.
+**/
+public class ThreadGate
+{
+ private boolean m_open = false;
+
+ /**
+ * Open the gate and release any waiting threads.
+ **/
+ public synchronized void open()
+ {
+ m_open = true;
+ notifyAll();
+ }
+
+ /**
+ * Wait for the gate to open.
+ * @throws java.lang.InterruptedException If the calling thread is interrupted;
+ * the gate still remains closed until opened.
+ **/
+ public synchronized void await(long timeout) throws InterruptedException
+ {
+ while (!m_open)
+ {
+ wait(timeout);
+ }
+ }
+}
\ No newline at end of file
diff --git a/framework/src/main/java/org/osgi/framework/SystemBundle.java b/framework/src/main/java/org/osgi/framework/SystemBundle.java
new file mode 100644
index 0000000..cc60222
--- /dev/null
+++ b/framework/src/main/java/org/osgi/framework/SystemBundle.java
@@ -0,0 +1,43 @@
+/*
+ * 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.osgi.framework;
+
+public interface SystemBundle extends Bundle
+{
+ public static final String FRAMEWORK_STORAGE_PROP = "org.osgi.framework.storage";
+
+ /**
+ * Initializes the System Bundle. After calling this method, the System
+ * Bundle must be in the STARTING state and must have a valid Bundle Context.
+ * The System Bundle will not actually be started until start() is called.
+ * If the System Bundle is not initialized called prior to calling start(),
+ * then the start method must initialize the System Bundle prior to starting.
+ **/
+ public void init() throws BundleException;
+
+ /**
+ * Wait until this System Bundle has completely stopped. The stop and update
+ * methods on a System Bundle perform an asynchronous stop of the System
+ * Bundle. This method can be used to wait until the asynchronous stop of
+ * this System Bundle has completed. This method will only wait if called
+ * when this System Bundle is in the STARTING, ACTIVE, or STOPPING states.
+ * Otherwise it will return immediately.
+ **/
+ public void waitForStop(long timeout) throws InterruptedException;
+}
\ No newline at end of file
diff --git a/main/src/main/java/org/apache/felix/main/Main.java b/main/src/main/java/org/apache/felix/main/Main.java
index 09ab34c..9a68dc7 100644
--- a/main/src/main/java/org/apache/felix/main/Main.java
+++ b/main/src/main/java/org/apache/felix/main/Main.java
@@ -26,6 +26,7 @@
import org.apache.felix.framework.Felix;
import org.apache.felix.framework.cache.BundleCache;
import org.apache.felix.framework.util.StringMap;
+import org.osgi.framework.SystemBundle;
/**
* <p>
@@ -181,6 +182,9 @@
// See if the profile directory property was specified.
String profileDirName = configProps.getProperty(BundleCache.CACHE_PROFILE_DIR_PROP);
+ profileDirName = (profileDirName == null)
+ ? configProps.getProperty(SystemBundle.FRAMEWORK_STORAGE_PROP)
+ : profileDirName;
// Print welcome banner.
System.out.println("\nWelcome to Felix.");
@@ -231,9 +235,12 @@
list.add(new AutoActivator(configProps));
// Create a case-insensitive property map.
Map configMap = new StringMap(configProps, false);
+ configMap.put("felix.systembundle.activators", list);
// Create an instance of the framework.
- m_felix = new Felix(configMap, list);
+ m_felix = new Felix(configMap);
m_felix.start();
+ m_felix.waitForStop(0);
+ System.exit(0);
}
catch (Exception ex)
{