in framework/src/main/java/org/apache/felix/framework/Felix.java [2123:2371]
void startBundle(BundleImpl bundle, int options) 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.
int eventType;
boolean isTransient = (options & Bundle.START_TRANSIENT) != 0;
// Acquire bundle lock.
try
{
acquireBundleLock(bundle,
Bundle.INSTALLED | Bundle.RESOLVED | Bundle.STARTING | Bundle.ACTIVE | Bundle.STOPPING);
}
catch (IllegalStateException ex)
{
if (bundle.getState() == Bundle.UNINSTALLED)
{
throw new IllegalStateException("Cannot start an uninstalled bundle.");
}
else
{
throw new BundleException(
"Bundle " + bundle
+ " cannot be started: " + ex.getMessage());
}
}
// Record whether the bundle is using its declared activation policy.
boolean wasDeferred = bundle.isDeclaredActivationPolicyUsed()
&& (((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
.getDeclaredActivationPolicy() == BundleRevisionImpl.LAZY_ACTIVATION);
bundle.setDeclaredActivationPolicyUsed(
(options & Bundle.START_ACTIVATION_POLICY) != 0);
BundleException rethrow = null;
try
{
// The spec doesn't say whether it is possible to start an extension
// We just do nothing
if (bundle.isExtension())
{
return;
}
// As per the OSGi spec, fragment bundles can not be started and must
// throw a BundleException when there is an attempt to start one.
if (Util.isFragment(bundle.adapt(BundleRevision.class)))
{
throw new BundleException("Fragment bundles can not be started.");
}
// Set and save the bundle's persistent state to active
// if we are supposed to record state change.
if (!isTransient)
{
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
// the framework's target start level and if so just return. For
// transient starts we compare their start level to the current
// active start level, since we never process transient starts
// asynchronously (i.e., they are either satisfied and process
// synchronously here or they throw an exception).
int bundleLevel = bundle.getStartLevel(getInitialBundleStartLevel());
if (isTransient && (bundleLevel > m_activeStartLevel))
{
// Throw an exception for transient starts.
throw new BundleException(
"Cannot start bundle " + bundle + " because its start level is "
+ bundleLevel
+ ", which is greater than the framework's start level of "
+ m_activeStartLevel + ".", BundleException.START_TRANSIENT_ERROR);
}
else if (bundleLevel > m_targetStartLevel)
{
// Ignore persistent starts that are not satisfied.
return;
}
// Check to see if there is a start level change in progress and if
// so queue this bundle to the start level bundle queue for the start
// level thread and return, except for transient starts which are
// queued but processed synchronously.
// Note: Don't queue starts from the start level thread, otherwise
// we'd never get anything started.
if (!Thread.currentThread().getName().equals(FrameworkStartLevelImpl.THREAD_NAME))
{
synchronized (m_startLevelBundles)
{
// Since we have the start level queue lock, we know the
// active start level cannot change. For transient starts
// we now need to double check and make sure we can still
// start them since technically the active start level could
// have changed.
if (isTransient && (bundleLevel > m_activeStartLevel))
{
throw new BundleException(
"Cannot start bundle " + bundle + " because its start level is "
+ bundleLevel
+ ", which is greater than the framework's start level of "
+ m_activeStartLevel + ".", BundleException.START_TRANSIENT_ERROR);
}
// If the start level bundle queue is not empty, then we know
// there is a start level operation ongoing. We know that the
// bundle being started is satisfied by the target start level
// otherwise we wouldn't be here. In most cases we simply want
// to queue the bundle; however, if the bundle level is lower
// than the current active start level, then we want to handle
// it synchronously. This is because the start level thread
// ignores bundles that it should have already processed.
// So queue the bundle if its bundle start level is greater
// or equal to the active start level and then simply return
// since it will be processed asynchronously.
if (!m_startLevelBundles.isEmpty()
&& (bundleLevel >= m_activeStartLevel))
{
// Only add the bundle to the start level bundles
// being process if it is not already there.
boolean found = false;
for (StartLevelTuple tuple : m_startLevelBundles)
{
if (tuple.m_bundle == bundle)
{
found = true;
}
}
if (!found)
{
m_startLevelBundles.add(new StartLevelTuple(bundle, bundleLevel));
}
// Note that although we queued the transiently started
// bundle, we don't return here because we handle all
// transient bundles synchronously. The reason why we
// queue it anyway is for the case where the start level
// is lowering, since the transiently started bundle may
// have already been processed by the start level thread
// and we will start it briefly here synchronously, but
// we want the start level thread to process it again
// so it gets another chance to stop it. This is not an
// issue when the start level is raising because the
// start level thread ignores non-persistently started
// bundles or if it is also persistently started it will
// be a no-op.
if (!isTransient)
{
return;
}
}
}
}
switch (bundle.getState())
{
case Bundle.UNINSTALLED:
throw new IllegalStateException("Cannot start an uninstalled bundle.");
case Bundle.STARTING:
if (!wasDeferred)
{
throw new BundleException(
"Bundle " + bundle
+ " cannot be started, since it is starting.");
}
break;
case Bundle.STOPPING:
throw new BundleException(
"Bundle " + bundle
+ " cannot be started, since it is stopping.");
case Bundle.ACTIVE:
return;
case Bundle.INSTALLED:
resolveBundleRevision(bundle.adapt(BundleRevision.class));
// No break.
case Bundle.RESOLVED:
// Set the bundle's context.
bundle.setBundleContext(new BundleContextImpl(m_logger, this, bundle));
// At this point, no matter if the bundle's activation policy is
// eager or deferred, we need to set the bundle's state to STARTING.
// We don't fire a BundleEvent here for this state change, since
// STARTING events are only fired if we are invoking the activator,
// which we may not do if activation is deferred.
setBundleStateAndNotify(bundle, Bundle.STARTING);
break;
}
// If the bundle's activation policy is eager or activation has already
// been triggered, then activate the bundle immediately.
if (!bundle.isDeclaredActivationPolicyUsed()
|| (((BundleRevisionImpl) bundle.adapt(BundleRevision.class))
.getDeclaredActivationPolicy() != BundleRevisionImpl.LAZY_ACTIVATION)
|| ((BundleClassLoader) bundle.adapt(BundleWiring.class).getClassLoader())
.isActivationTriggered())
{
// Record the event type for the final event and activate.
eventType = BundleEvent.STARTED;
// Note that the STARTING event is thrown in the activateBundle() method.
try
{
activateBundle(bundle, false);
}
catch (BundleException ex)
{
rethrow = ex;
}
}
// Otherwise, defer bundle activation.
else
{
// Record the event type for the final event.
eventType = BundleEvent.LAZY_ACTIVATION;
}
// 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
// or LAZY_ACTIVATION event here without holding the lock. Otherwise,
// fire STOPPED and rethrow exception.
if (rethrow == null)
{
fireBundleEvent(eventType, bundle);
}
else
{
fireBundleEvent(BundleEvent.STOPPED, bundle);
throw rethrow;
}
}