in framework/src/main/java/org/apache/felix/framework/Felix.java [1410:1635]
void setActiveStartLevel(int requestedLevel, FrameworkListener[] listeners)
{
Bundle[] bundles;
// Record the target start level immediately and use this for
// comparisons for starting/stopping bundles to avoid race
// conditions to restart stopped bundles.
m_targetStartLevel = requestedLevel;
// Do nothing if the requested start level is the same as the
// active start level.
if (m_targetStartLevel != m_activeStartLevel)
{
// Synchronization for changing the start level is rather loose.
// The framework's active start level is volatile, so no lock is
// needed to access it. No locks are held while processing the
// currently installed bundles for starting/stopping based on the new
// active start level. The only locking that occurs is for individual
// bundles when startBundle()/stopBundle() is called, but this locking
// is done in the respective method.
//
// This approach does mean that it is possible for a for individual
// bundle states to change during this operation. If a bundle's start
// level changes, then it is possible for it to be processed out of
// order. Uninstalled bundles are just logged and ignored.
//
// Calls to this method are only made by the start level thread, which
// serializes framework start level changes. Thus, it is not possible
// for two requests to change the framework's start level to interfere
// with each other.
// Acquire global lock.
boolean locked = acquireGlobalLock();
if (!locked)
{
// If the calling thread holds bundle locks, then we might not
// be able to get the global lock.
throw new IllegalStateException(
"Unable to acquire global lock to create bundle snapshot.");
}
boolean bundlesRemaining;
try
{
synchronized (m_startLevelBundles)
{
// Get a sorted snapshot of all installed bundles
// to be processed during the start level change.
// We also snapshot the start level here, since it
// may change and we don't want to consider any
// changes since they will be queued for the start
// level thread.
bundles = getBundles();
for (Bundle b : bundles)
{
m_startLevelBundles.add(
new StartLevelTuple(
(BundleImpl) b,
((BundleImpl) b).getStartLevel(
getInitialBundleStartLevel())));
}
bundlesRemaining = !m_startLevelBundles.isEmpty();
}
}
finally
{
releaseGlobalLock();
}
// Determine if we are lowering or raising the
// active start level.
boolean isLowering = (m_targetStartLevel < m_activeStartLevel);
// Determine the range of start levels to process.
int low = (isLowering) ? m_targetStartLevel + 1 : m_activeStartLevel + 1;
int high = (isLowering) ? m_activeStartLevel : m_targetStartLevel;
m_activeStartLevel = (isLowering) ? high : low;
// Process bundles and stop or start them accordingly.
while (bundlesRemaining)
{
StartLevelTuple tuple;
// Remove our tuple to be processed while holding the queue lock
// and update the active start level accordingly, which allows
// us to determine in startBundle() if concurrent requests to
// start a bundle should be handled synchronously or just added
// to the queue and handled asynchronously.
synchronized (m_startLevelBundles)
{
if (isLowering)
{
tuple = m_startLevelBundles.last();
}
else
{
tuple = m_startLevelBundles.first();
}
if ((tuple.m_level >= low) && (tuple.m_level <= high))
{
m_activeStartLevel = tuple.m_level;
}
}
// Ignore the system bundle, since its start() and
// stop() methods get called explicitly in Felix.start()
// and Felix.stop(), respectively.
if (tuple.m_bundle.getBundleId() != 0)
{
// Lock the current bundle.
try
{
acquireBundleLock(tuple.m_bundle,
Bundle.INSTALLED | Bundle.RESOLVED | Bundle.ACTIVE
| Bundle.STARTING | Bundle.STOPPING);
}
catch (IllegalStateException ex)
{
// Ignore if the bundle has been uninstalled.
if (tuple.m_bundle.getState() != Bundle.UNINSTALLED)
{
fireFrameworkEvent(FrameworkEvent.ERROR, tuple.m_bundle, ex);
m_logger.log(tuple.m_bundle,
Logger.LOG_ERROR,
"Error locking " + tuple.m_bundle._getLocation(), ex);
}
else
{
synchronized (m_startLevelBundles)
{
m_startLevelBundles.remove(tuple);
bundlesRemaining = !m_startLevelBundles.isEmpty();
}
}
continue;
}
try
{
// Start the bundle if necessary.
// Note that we only attempt to start the bundle if
// its start level is equal to the active start level,
// which means we assume lower bundles are in the state
// they should be in (i.e., we won't attempt to restart
// them if they previously failed to start).
if (!isLowering
&& (((tuple.m_bundle.getPersistentState() == Bundle.ACTIVE)
|| (tuple.m_bundle.getPersistentState() == Bundle.STARTING))
&& (tuple.m_level == m_activeStartLevel)))
{
try
{
// TODO: LAZY - Not sure if this is the best way...
int options = Bundle.START_TRANSIENT;
options = (tuple.m_bundle.getPersistentState() == Bundle.STARTING)
? options | Bundle.START_ACTIVATION_POLICY
: options;
startBundle(tuple.m_bundle, options);
}
catch (Throwable th)
{
fireFrameworkEvent(FrameworkEvent.ERROR, tuple.m_bundle, th);
m_logger.log(tuple.m_bundle,
Logger.LOG_ERROR,
"Error starting " + tuple.m_bundle._getLocation(), th);
}
}
// Stop the bundle if necessary.
else if (isLowering
&& (((tuple.m_bundle.getState() == Bundle.ACTIVE)
|| (tuple.m_bundle.getState() == Bundle.STARTING))
&& (tuple.m_level == m_activeStartLevel)))
{
try
{
stopBundle(tuple.m_bundle, false);
}
catch (Throwable th)
{
fireFrameworkEvent(FrameworkEvent.ERROR, tuple.m_bundle, th);
m_logger.log(tuple.m_bundle,
Logger.LOG_ERROR,
"Error stopping " + tuple.m_bundle._getLocation(), th);
}
}
}
finally
{
// Always release bundle lock.
releaseBundleLock(tuple.m_bundle);
}
}
synchronized (m_startLevelBundles)
{
m_startLevelBundles.remove(tuple);
bundlesRemaining = !m_startLevelBundles.isEmpty();
}
}
m_activeStartLevel = m_targetStartLevel;
}
if (getState() == Bundle.ACTIVE)
{
fireFrameworkEvent(FrameworkEvent.STARTLEVEL_CHANGED, this, null);
if (listeners != null)
{
FrameworkEvent event = new FrameworkEvent(
FrameworkEvent.STARTLEVEL_CHANGED, this, null);
for (FrameworkListener l : listeners)
{
try
{
l.frameworkEvent(event);
}
catch (Throwable th)
{
m_logger.log(Logger.LOG_ERROR,
"Framework listener delivery error.", th);
}
}
}
}
}