Initial work on activation policies. (FELIX-749)
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@782727 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 5f17330..85b80c9 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleImpl.java
@@ -26,7 +26,7 @@
import org.apache.felix.framework.cache.BundleArchive;
import org.apache.felix.framework.ext.SecurityProvider;
-import org.apache.felix.framework.searchpolicy.ModuleImpl;
+import org.apache.felix.framework.ModuleImpl;
import org.apache.felix.moduleloader.IModule;
import org.osgi.framework.*;
@@ -38,6 +38,7 @@
private final BundleArchive m_archive;
private IModule[] m_modules = new IModule[0];
private volatile int m_state;
+ private int m_runtimeActivationPolicy;
private BundleActivator m_activator = null;
private BundleContext m_context = null;
private final Map m_cachedHeaders = new HashMap();
@@ -62,6 +63,7 @@
__m_felix = null;
m_archive = null;
m_state = Bundle.INSTALLED;
+ m_runtimeActivationPolicy = 0;
m_stale = false;
m_activator = null;
m_context = null;
@@ -72,6 +74,7 @@
__m_felix = felix;
m_archive = archive;
m_state = Bundle.INSTALLED;
+ m_runtimeActivationPolicy = 0;
m_stale = false;
m_activator = null;
m_context = null;
@@ -128,6 +131,18 @@
}
}
+ synchronized int getRuntimeActivationPolicy()
+ {
+ return (m_runtimeActivationPolicy == IModule.EAGER_ACTIVATION)
+ ? IModule.EAGER_ACTIVATION
+ : getCurrentModule().getDeclaredActivationPolicy();
+ }
+
+ synchronized void setRuntimeActivationPolicy(int policy)
+ {
+ m_runtimeActivationPolicy = policy;
+ }
+
synchronized BundleActivator getActivator()
{
return m_activator;
@@ -647,6 +662,20 @@
}
}
+ void setPersistentStateStarting()
+ {
+ try
+ {
+ m_archive.setPersistentState(Bundle.STARTING);
+ }
+ catch (Exception ex)
+ {
+ getFramework().getLogger().log(
+ Logger.LOG_ERROR,
+ "Error writing persistent state to bundle archive.",
+ ex);
+ }
+ }
void setPersistentStateUninstalled()
{
try
@@ -762,12 +791,6 @@
public void start(int options) throws BundleException
{
- if ((options & Bundle.START_ACTIVATION_POLICY) > 0)
- {
- throw new UnsupportedOperationException(
- "The activation policy feature has not yet been implemented.");
- }
-
Object sm = System.getSecurityManager();
if (sm != null)
@@ -776,7 +799,7 @@
AdminPermission.EXECUTE));
}
- getFramework().startBundle(this, ((options & Bundle.START_TRANSIENT) == 0));
+ getFramework().startBundle(this, options);
}
public void update() throws BundleException
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 1631ecc..832e1e9 100644
--- a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
+++ b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
@@ -37,7 +37,7 @@
import java.util.Set;
import org.apache.felix.framework.Felix.FelixResolver;
-import org.apache.felix.framework.searchpolicy.ModuleImpl;
+import org.apache.felix.framework.ModuleImpl;
import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.util.StringMap;
import org.apache.felix.framework.util.Util;
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 3ac2488..83d4b79 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -27,7 +27,7 @@
import org.apache.felix.framework.cache.*;
import org.apache.felix.framework.ext.SecurityProvider;
import org.apache.felix.framework.searchpolicy.*;
-import org.apache.felix.framework.searchpolicy.ModuleImpl.ModuleClassLoader;
+import org.apache.felix.framework.ModuleImpl.ModuleClassLoader;
import org.apache.felix.framework.util.*;
import org.apache.felix.framework.util.manifestparser.*;
import org.apache.felix.moduleloader.*;
@@ -981,13 +981,19 @@
try
{
// Start the bundle if necessary.
- if ((impl.getPersistentState() == Bundle.ACTIVE) &&
- (impl.getStartLevel(getInitialBundleStartLevel())
+ if (((impl.getPersistentState() == Bundle.ACTIVE)
+ || (impl.getPersistentState() == Bundle.STARTING))
+ && (impl.getStartLevel(getInitialBundleStartLevel())
<= getActiveStartLevel()))
{
try
{
- startBundle(impl, false);
+// TODO: LAZY - Not sure if this is the best way...
+ int options = Bundle.START_TRANSIENT;
+ options = (impl.getPersistentState() == Bundle.STARTING)
+ ? options | Bundle.START_ACTIVATION_POLICY
+ : options;
+ startBundle(impl, options);
}
catch (Throwable th)
{
@@ -1139,11 +1145,18 @@
try
{
// Start the bundle if necessary.
- if ((impl.getPersistentState() == Bundle.ACTIVE) &&
- (impl.getStartLevel(getInitialBundleStartLevel())
+ // Start the bundle if necessary.
+ if (((impl.getPersistentState() == Bundle.ACTIVE)
+ || (impl.getPersistentState() == Bundle.STARTING))
+ && (impl.getStartLevel(getInitialBundleStartLevel())
<= getActiveStartLevel()))
{
- startBundle(impl, false);
+// TODO: LAZY - Not sure if this is the best way...
+ int options = Bundle.START_TRANSIENT;
+ options = (impl.getPersistentState() == Bundle.STARTING)
+ ? options | Bundle.START_ACTIVATION_POLICY
+ : options;
+ startBundle(impl, options);
}
// Stop the bundle if necessary.
else if ((impl.getState() == Bundle.ACTIVE) &&
@@ -1192,7 +1205,8 @@
throw new IllegalArgumentException("Bundle is uninstalled.");
}
- return (((BundleImpl) bundle).getPersistentState() == Bundle.ACTIVE);
+ return (((BundleImpl) bundle).getPersistentState() == Bundle.ACTIVE)
+ || (((BundleImpl) bundle).getPersistentState() == Bundle.STARTING);
}
//
@@ -1363,7 +1377,7 @@
/**
* Implementation for Bundle.start().
**/
- void startBundle(BundleImpl bundle, boolean record) throws BundleException
+ void startBundle(BundleImpl bundle, int options) throws BundleException
{
// CONCURRENCY NOTE:
// We will first acquire the bundle lock for the specific bundle
@@ -1390,6 +1404,13 @@
+ " cannot be started, since it is either starting or stopping.");
}
}
+
+ // Set the bundl
+ bundle.setRuntimeActivationPolicy(
+ ((options & Bundle.START_ACTIVATION_POLICY) > 0)
+ ? IModule.LAZY_ACTIVATION
+ : IModule.EAGER_ACTIVATION);
+
try
{
// The spec doesn't say whether it is possible to start an extension
@@ -1408,9 +1429,16 @@
// Set and save the bundle's persistent state to active
// if we are supposed to record state change.
- if (record)
+ if ((options & Bundle.START_TRANSIENT) == 0)
{
- bundle.setPersistentStateActive();
+ if ((options & Bundle.START_ACTIVATION_POLICY) != 0)
+ {
+ bundle.setPersistentStateStarting();
+ }
+ else
+ {
+ bundle.setPersistentStateActive();
+ }
}
// Check to see if the bundle's start level is greater than the
@@ -1418,7 +1446,7 @@
if (bundle.getStartLevel(getInitialBundleStartLevel()) > getActiveStartLevel())
{
// Throw an exception for transient starts.
- if (!record)
+ if ((options & Bundle.START_TRANSIENT) != 0)
{
throw new BundleException(
"Cannot start bundle " + bundle + " because its start level is "
@@ -1450,11 +1478,64 @@
break;
}
+ // Set the bundle's context.
+ bundle.setBundleContext(new BundleContextImpl(m_logger, this, bundle));
+
+ if (bundle.getRuntimeActivationPolicy() != IModule.LAZY_ACTIVATION)
+ {
+ activateBundle(bundle);
+ }
+ else
+ {
+ setBundleStateAndNotify(bundle, Bundle.STARTING);
+ }
+
+ // We still need to fire the STARTED event, but we will do
+ // it later so we can release the bundle lock.
+ }
+ finally
+ {
+ // Release bundle lock.
+ releaseBundleLock(bundle);
+ }
+
+ // If there was no exception, then we should fire the STARTED event
+ // here without holding the lock.
+// TODO: LAZY - WE SHOULD REALLY BE FIRING EVENT HERE, OUTSIDE OF LOCK.
+// fireBundleEvent(BundleEvent.STARTED, bundle);
+ }
+
+ public void activateBundle(BundleImpl bundle) throws BundleException
+ {
+ // CONCURRENCY NOTE:
+ // We will first acquire the bundle lock for the specific bundle
+ // as long as the bundle is INSTALLED, RESOLVED, or ACTIVE. If this
+ // bundle is not yet resolved, then it will be resolved too. In
+ // that case, the global lock will be acquired to make sure no
+ // bundles can be installed or uninstalled during the resolve.
+
+ // Acquire bundle lock.
+ try
+ {
+ acquireBundleLock(bundle, Bundle.STARTING);
+ }
+ catch (IllegalStateException ex)
+ {
+ throw new IllegalStateException(
+ "Activation only occurs for bundles in STARTING state.");
+ }
+ try
+ {
+ // Check to see if the bundle's start level is greater than the
+ // the framework's active start level.
+ if (bundle.getStartLevel(getInitialBundleStartLevel()) > getActiveStartLevel())
+ {
+ // Ignore persistent starts.
+ return;
+ }
+
try
{
- // Set the bundle's context.
- bundle.setBundleContext(new BundleContextImpl(m_logger, this, bundle));
-
// Set the bundle's activator.
bundle.setActivator(createBundleActivator(bundle));
@@ -1520,6 +1601,7 @@
// If there was no exception, then we should fire the STARTED event
// here without holding the lock.
+// TODO: LAZY - WE COULD BE HOLDING THE LOCK FROM startBundle() ABOVE.
fireBundleEvent(BundleEvent.STARTED, bundle);
}
@@ -1725,7 +1807,7 @@
// but do not change its persistent state.
else if (oldState == Bundle.ACTIVE)
{
- startBundle(bundle, false);
+ startBundle(bundle, Bundle.START_TRANSIENT);
}
// If update failed, rethrow exception.
@@ -1763,7 +1845,8 @@
// Acquire bundle lock.
try
{
- acquireBundleLock(bundle, Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE);
+ acquireBundleLock(bundle,
+ Bundle.INSTALLED | Bundle.RESOLVED | Bundle.STARTING | Bundle.ACTIVE);
}
catch (IllegalStateException ex)
{
@@ -1775,7 +1858,7 @@
{
throw new BundleException(
"Bundle " + bundle
- + " cannot be stopped, since it is either starting or stopping.");
+ + " cannot be stopped since it is already stopping.");
}
}
@@ -1801,6 +1884,12 @@
case Bundle.UNINSTALLED:
throw new IllegalStateException("Cannot stop an uninstalled bundle.");
case Bundle.STARTING:
+ if (bundle.getRuntimeActivationPolicy() != IModule.LAZY_ACTIVATION)
+ {
+ throw new BundleException(
+ "Stopping a starting or stopping bundle is currently not supported.");
+ }
+ break;
case Bundle.STOPPING:
throw new BundleException(
"Stopping a starting or stopping bundle is currently not supported.");
@@ -1814,17 +1903,20 @@
break;
}
- try
+ if (bundle.getState() != Bundle.STARTING)
{
- if (bundle.getActivator() != null)
+ try
{
- m_secureAction.stopActivator(bundle.getActivator(), bundle.getBundleContext());
+ if (bundle.getActivator() != null)
+ {
+ m_secureAction.stopActivator(bundle.getActivator(), bundle.getBundleContext());
+ }
}
- }
- catch (Throwable th)
- {
- m_logger.log(Logger.LOG_ERROR, "Error stopping bundle.", th);
- rethrow = th;
+ catch (Throwable th)
+ {
+ m_logger.log(Logger.LOG_ERROR, "Error stopping bundle.", th);
+ rethrow = th;
+ }
}
// Do not clean up after the system bundle since it will
@@ -1894,7 +1986,8 @@
// Acquire bundle lock.
try
{
- acquireBundleLock(bundle, Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE);
+ acquireBundleLock(bundle,
+ Bundle.INSTALLED | Bundle.RESOLVED | Bundle.STARTING | Bundle.ACTIVE);
}
catch (IllegalStateException ex)
{
@@ -1906,7 +1999,7 @@
{
throw new BundleException(
"Bundle " + bundle
- + " cannot be uninstalled, since it is either starting or stopping.");
+ + " cannot be uninstalled since it is stopping.");
}
}
@@ -4141,7 +4234,12 @@
{
try
{
- startBundle(m_bundle, false);
+// TODO: LAZY - Not sure if this is the best way...
+ int options = Bundle.START_TRANSIENT;
+ options = (m_bundle.getPersistentState() == Bundle.STARTING)
+ ? options | Bundle.START_ACTIVATION_POLICY
+ : options;
+ startBundle(m_bundle, options);
}
catch (Throwable ex)
{
diff --git a/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java b/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java
index 6760a1c..c2aff03 100644
--- a/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java
+++ b/framework/src/main/java/org/apache/felix/framework/FelixResolverState.java
@@ -24,7 +24,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import org.apache.felix.framework.searchpolicy.ModuleImpl;
+import org.apache.felix.framework.ModuleImpl;
import org.apache.felix.framework.searchpolicy.Resolver;
import org.apache.felix.framework.searchpolicy.PackageSource;
import org.apache.felix.framework.util.Util;
diff --git a/framework/src/main/java/org/apache/felix/framework/searchpolicy/ModuleImpl.java b/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
similarity index 95%
rename from framework/src/main/java/org/apache/felix/framework/searchpolicy/ModuleImpl.java
rename to framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
index f1e8338..22bf794 100644
--- a/framework/src/main/java/org/apache/felix/framework/searchpolicy/ModuleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/ModuleImpl.java
@@ -16,8 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.framework.searchpolicy;
+package org.apache.felix.framework;
+import org.apache.felix.framework.searchpolicy.*;
import org.apache.felix.moduleloader.*;
import java.io.IOException;
import java.io.InputStream;
@@ -42,7 +43,6 @@
import java.util.Set;
import java.util.Vector;
import org.apache.felix.framework.Felix.FelixResolver;
-import org.apache.felix.framework.Logger;
import org.apache.felix.framework.cache.JarContent;
import org.apache.felix.framework.util.CompoundEnumeration;
import org.apache.felix.framework.util.FelixConstants;
@@ -77,7 +77,7 @@
private final IRequirement[] m_requirements;
private final IRequirement[] m_dynamicRequirements;
private final R4Library[] m_nativeLibraries;
- private final int m_activationPolicy;
+ private final int m_declaredActivationPolicy;
private final Bundle m_bundle;
private IModule[] m_fragments = null;
@@ -90,6 +90,7 @@
private IContent[] m_contentPath;
private IContent[] m_fragmentContents = null;
private ModuleClassLoader m_classLoader;
+ private boolean m_isActivationTriggered = false;
private ProtectionDomain m_protectionDomain = null;
private static SecureAction m_secureAction = new SecureAction();
@@ -135,7 +136,7 @@
m_requirements = null;
m_dynamicRequirements = null;
m_nativeLibraries = null;
- m_activationPolicy = EAGER_ACTIVATION;
+ m_declaredActivationPolicy = EAGER_ACTIVATION;
}
public ModuleImpl(
@@ -167,7 +168,7 @@
m_requirements = mp.getRequirements();
m_dynamicRequirements = mp.getDynamicRequirements();
m_nativeLibraries = mp.getLibraries();
- m_activationPolicy = mp.getActivationPolicy();
+ m_declaredActivationPolicy = mp.getActivationPolicy();
m_symbolicName = mp.getSymbolicName();
m_isExtension = mp.isExtension();
@@ -302,9 +303,9 @@
return m_nativeLibraries;
}
- public int getActivationPolicy()
+ public int getDeclaredActivationPolicy()
{
- return m_activationPolicy;
+ return m_declaredActivationPolicy;
}
//
@@ -1171,6 +1172,11 @@
return m_id;
}
+ private synchronized boolean isActivationTrigger()
+ {
+ return m_isActivationTriggered;
+ }
+
private synchronized ModuleClassLoader getClassLoader()
{
if (m_classLoader == null)
@@ -1443,15 +1449,14 @@
m_dexFileClassLoadClass = dexFileClassLoadClass;
}
+ private static final ThreadLocal m_local = new ThreadLocal();
+
public class ModuleClassLoader extends SecureClassLoader
{
private final Map m_jarContentToDexFile;
public ModuleClassLoader()
{
- // Set parent class loader to the same class loader
- // used for boot delegation.
- super(ModuleImpl.this.getClass().getClassLoader());
if (m_dexFileClassLoadClass != null)
{
m_jarContentToDexFile = new HashMap();
@@ -1543,6 +1548,23 @@
if (clazz == null)
{
+ int activationPolicy = ((BundleImpl) getBundle()).getRuntimeActivationPolicy();
+
+ // If the module is using deferred activation, then if
+ // we load this class from this module we need to activate
+ // the module before returning the class.
+ if (!m_isActivationTriggered
+ && (activationPolicy == IModule.LAZY_ACTIVATION)
+ && (getBundle().getState() == Bundle.STARTING))
+ {
+ List list = (List) m_local.get();
+ if (list == null)
+ {
+ list = new ArrayList();
+ m_local.set(list);
+ }
+ list.add(new Object[] { name, getBundle() });
+ }
// We need to try to define a Package object for the class
// before we call defineClass(). Get the package name and
// see if we have already created the package.
@@ -1600,8 +1622,37 @@
clazz = defineClass(name, bytes, 0, bytes.length);
}
}
+
+ // At this point if we have a class, then the deferred
+ // activation trigger has tripped.
+ if (!m_isActivationTriggered && (clazz != null))
+ {
+ m_isActivationTriggered = true;
+ }
}
}
+
+ // Perform deferred activation without holding the class loader lock,
+ // if necessary.
+ List list = (List) m_local.get();
+ if ((list != null)
+ && (list.size() > 0)
+ && ((Object[]) list.get(0))[0].equals(name))
+ {
+ for (int i = list.size() - 1; i >= 0; i--)
+ {
+ try
+ {
+ ((BundleImpl) ((Object[]) list.get(i))[1]).getFramework().activateBundle(
+ (BundleImpl) ((Object[]) list.get(i))[1]);
+ }
+ catch (BundleException ex)
+ {
+ ex.printStackTrace();
+ }
+ }
+ list.clear();
+ }
}
}
diff --git a/framework/src/main/java/org/apache/felix/framework/PackageAdminImpl.java b/framework/src/main/java/org/apache/felix/framework/PackageAdminImpl.java
index cd7bc38..1a5a9fe 100644
--- a/framework/src/main/java/org/apache/felix/framework/PackageAdminImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/PackageAdminImpl.java
@@ -20,7 +20,7 @@
import java.util.*;
-import org.apache.felix.framework.searchpolicy.ModuleImpl;
+import org.apache.felix.framework.ModuleImpl;
import org.apache.felix.framework.util.Util;
import org.apache.felix.framework.util.VersionRange;
import org.apache.felix.moduleloader.IModule;
diff --git a/framework/src/main/java/org/apache/felix/framework/RequiredBundleImpl.java b/framework/src/main/java/org/apache/felix/framework/RequiredBundleImpl.java
index 8b77274..60694cf 100644
--- a/framework/src/main/java/org/apache/felix/framework/RequiredBundleImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/RequiredBundleImpl.java
@@ -22,7 +22,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import org.apache.felix.framework.searchpolicy.ModuleImpl;
+import org.apache.felix.framework.ModuleImpl;
import org.apache.felix.framework.util.Util;
import org.apache.felix.moduleloader.ICapability;
import org.apache.felix.moduleloader.IModule;
diff --git a/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java b/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java
index cc6bc9c..8e64020 100644
--- a/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java
+++ b/framework/src/main/java/org/apache/felix/framework/cache/BundleArchive.java
@@ -78,6 +78,7 @@
private static final transient String REVISION_DIRECTORY = "version";
private static final transient String DATA_DIRECTORY = "data";
private static final transient String ACTIVE_STATE = "active";
+ private static final transient String STARTING_STATE = "starting";
private static final transient String INSTALLED_STATE = "installed";
private static final transient String UNINSTALLED_STATE = "uninstalled";
@@ -313,6 +314,10 @@
{
m_persistentState = Bundle.ACTIVE;
}
+ else if ((s != null) && s.equals(STARTING_STATE))
+ {
+ m_persistentState = Bundle.STARTING;
+ }
else if ((s != null) && s.equals(UNINSTALLED_STATE))
{
m_persistentState = Bundle.UNINSTALLED;
@@ -355,6 +360,9 @@
case Bundle.ACTIVE:
s = ACTIVE_STATE;
break;
+ case Bundle.STARTING:
+ s = STARTING_STATE;
+ break;
case Bundle.UNINSTALLED:
s = UNINSTALLED_STATE;
break;
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 343b6e1..fcf3a4c 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
@@ -25,7 +25,7 @@
import java.util.Hashtable;
import java.util.jar.JarFile;
-import org.apache.felix.framework.searchpolicy.ModuleImpl;
+import org.apache.felix.framework.ModuleImpl;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
diff --git a/framework/src/main/java/org/apache/felix/moduleloader/IModule.java b/framework/src/main/java/org/apache/felix/moduleloader/IModule.java
index 833ca54..57a9018 100644
--- a/framework/src/main/java/org/apache/felix/moduleloader/IModule.java
+++ b/framework/src/main/java/org/apache/felix/moduleloader/IModule.java
@@ -44,7 +44,7 @@
IRequirement[] getRequirements();
IRequirement[] getDynamicRequirements();
R4Library[] getNativeLibraries();
- int getActivationPolicy();
+ int getDeclaredActivationPolicy();
// Run-time data access methods.
Bundle getBundle();