blob: dacad1b698972b96214764c7f3b24ef89a8a906e [file] [log] [blame]
/*
* 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.scr.impl.manager;
import java.security.Permission;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.felix.scr.Component;
import org.apache.felix.scr.Reference;
import org.apache.felix.scr.impl.Activator;
import org.apache.felix.scr.impl.BundleComponentActivator;
import org.apache.felix.scr.impl.config.ScrConfiguration;
import org.apache.felix.scr.impl.helper.ComponentMethods;
import org.apache.felix.scr.impl.helper.MethodResult;
import org.apache.felix.scr.impl.helper.SimpleLogger;
import org.apache.felix.scr.impl.metadata.ComponentMetadata;
import org.apache.felix.scr.impl.metadata.ReferenceMetadata;
import org.apache.felix.scr.impl.metadata.ServiceMetadata;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServicePermission;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.ComponentConstants;
import org.osgi.service.log.LogService;
/**
* The default ComponentManager. Objects of this class are responsible for managing
* implementation object's lifecycle.
*
*/
public abstract class AbstractComponentManager<S> implements Component, SimpleLogger
{
//useful text for deactivation reason numbers
static final String[] REASONS = {"Unspecified",
"Component disabled",
"Reference became unsatisfied",
"Configuration modified",
"Configuration deleted",
"Component disabled",
"Bundle stopped"};
private final boolean m_factoryInstance;
// the ID of this component
private long m_componentId;
// The metadata
private final ComponentMetadata m_componentMetadata;
private final ComponentMethods m_componentMethods;
// The dependency managers that manage every dependency
private final List<DependencyManager<S, ?>> m_dependencyManagers;
private volatile boolean m_dependencyManagersInitialized;
private volatile boolean m_dependenciesCollected;
private final AtomicInteger m_trackingCount = new AtomicInteger( );
// A reference to the BundleComponentActivator
private BundleComponentActivator m_activator;
// The ServiceRegistration is now tracked in the RegistrationManager
private final ReentrantLock m_stateLock;
protected volatile boolean m_enabled;
protected final AtomicReference< CountDownLatch> m_enabledLatchRef = new AtomicReference<CountDownLatch>( new CountDownLatch(0) );
protected volatile boolean m_internalEnabled;
protected volatile boolean m_disposed;
//service event tracking
private int m_floor;
private volatile int m_ceiling;
private final Lock m_missingLock = new ReentrantLock();
private final Condition m_missingCondition = m_missingLock.newCondition();
private final Set<Integer> m_missing = new TreeSet<Integer>( );
volatile boolean m_activated;
protected final ReentrantReadWriteLock m_activationLock = new ReentrantReadWriteLock();
/**
* The constructor receives both the activator and the metadata
*
* @param activator
* @param metadata
* @param componentMethods
*/
protected AbstractComponentManager( BundleComponentActivator activator, ComponentMetadata metadata, ComponentMethods componentMethods )
{
this( activator, metadata, componentMethods, false );
}
protected AbstractComponentManager( BundleComponentActivator activator, ComponentMetadata metadata, ComponentMethods componentMethods, boolean factoryInstance )
{
m_factoryInstance = factoryInstance;
m_activator = activator;
m_componentMetadata = metadata;
this.m_componentMethods = componentMethods;
m_componentId = -1;
m_dependencyManagers = loadDependencyManagers( metadata );
m_stateLock = new ReentrantLock( true );
// dump component details
if ( isLogEnabled( LogService.LOG_DEBUG ) )
{
log(
LogService.LOG_DEBUG,
"Component {0} created: DS={1}, implementation={2}, immediate={3}, default-enabled={4}, factory={5}, configuration-policy={6}, activate={7}, deactivate={8}, modified={9} configuration-pid={10}",
new Object[]
{ metadata.getName(), metadata.getNamespaceCode(),
metadata.getImplementationClassName(), metadata.isImmediate(),
metadata.isEnabled(), metadata.getFactoryIdentifier(),
metadata.getConfigurationPolicy(), metadata.getActivate(), metadata.getDeactivate(),
metadata.getModified(), metadata.getConfigurationPid() }, null );
if ( metadata.getServiceMetadata() != null )
{
log( LogService.LOG_DEBUG, "Component {0} Services: servicefactory={1}, services={2}", new Object[]
{ metadata.getName(), Boolean.valueOf( metadata.getServiceMetadata().isServiceFactory() ),
Arrays.asList( metadata.getServiceMetadata().getProvides() ) }, null );
}
if ( metadata.getProperties() != null )
{
log( LogService.LOG_DEBUG, "Component {0} Properties: {1}", new Object[]
{ metadata.getName(), metadata.getProperties() }, null );
}
}
}
final long getLockTimeout()
{
BundleComponentActivator activator = getActivator();
if ( activator != null )
{
return activator.getConfiguration().lockTimeout();
}
return ScrConfiguration.DEFAULT_LOCK_TIMEOUT_MILLISECONDS;
}
private void obtainLock( Lock lock, String source )
{
try
{
if (!lock.tryLock( getLockTimeout(), TimeUnit.MILLISECONDS ) )
{
dumpThreads();
throw new IllegalStateException( "Could not obtain lock" );
}
}
catch ( InterruptedException e )
{
try
{
if (!lock.tryLock( getLockTimeout(), TimeUnit.MILLISECONDS ) )
{
dumpThreads();
throw new IllegalStateException( "Could not obtain lock" );
}
}
catch ( InterruptedException e1 )
{
Thread.currentThread().interrupt();
//TODO is there a better exception to throw?
throw new IllegalStateException( "Interrupted twice: Could not obtain lock" );
}
Thread.currentThread().interrupt();
}
}
final void obtainActivationReadLock( String source )
{
obtainLock( m_activationLock.readLock(), source);
}
final void releaseActivationReadLock( String source )
{
m_activationLock.readLock().unlock();
}
final void obtainActivationWriteLock( String source )
{
obtainLock( m_activationLock.writeLock(), source);
}
final void releaseActivationWriteeLock( String source )
{
if ( m_activationLock.getWriteHoldCount() > 0 )
{
m_activationLock.writeLock().unlock();
}
}
final void obtainStateLock( String source )
{
obtainLock( m_stateLock, source );
}
final void releaseStateLock( String source )
{
m_stateLock.unlock();
}
final boolean isStateLocked()
{
return m_stateLock.getHoldCount() > 0;
}
final void dumpThreads()
{
try
{
String dump = new ThreadDump().call();
log( LogService.LOG_DEBUG, dump, null );
}
catch ( Throwable t )
{
log( LogService.LOG_DEBUG, "Could not dump threads", t );
}
}
//service event tracking
void tracked( int trackingCount )
{
m_missingLock.lock();
try
{
if (trackingCount == m_floor + 1 )
{
m_floor++;
m_missing.remove( trackingCount );
}
else if ( trackingCount < m_ceiling )
{
m_missing.remove( trackingCount );
}
if ( trackingCount > m_ceiling )
{
for (int i = m_ceiling + 1; i < trackingCount; i++ )
{
m_missing.add( i );
}
m_ceiling = trackingCount;
}
m_missingCondition.signalAll();
}
finally
{
m_missingLock.unlock();
}
}
/**
* We effectively maintain the set of completely processed service event tracking counts. This method waits for all events prior
* to the parameter tracking count to complete, then returns. See further documentation in EdgeInfo.
* @param trackingCount
*/
void waitForTracked( int trackingCount )
{
m_missingLock.lock();
try
{
while ( m_ceiling < trackingCount || ( !m_missing.isEmpty() && m_missing.iterator().next() < trackingCount))
{
log( LogService.LOG_DEBUG, "waitForTracked trackingCount: {0} ceiling: {1} missing: {2}",
new Object[] {trackingCount, m_ceiling, m_missing}, null );
try
{
if ( !doMissingWait())
{
return;
}
}
catch ( InterruptedException e )
{
try
{
if ( !doMissingWait())
{
return;
}
}
catch ( InterruptedException e1 )
{
log( LogService.LOG_ERROR, "waitForTracked interrupted twice: {0} ceiling: {1} missing: {2}, Expect further errors",
new Object[] {trackingCount, m_ceiling, m_missing}, e1 );
}
Thread.currentThread().interrupt();
}
}
}
finally
{
m_missingLock.unlock();
}
}
private boolean doMissingWait() throws InterruptedException
{
if ( !m_missingCondition.await( getLockTimeout(), TimeUnit.MILLISECONDS ))
{
log( LogService.LOG_ERROR, "waitForTracked timed out: {0} ceiling: {1} missing: {2}, Expect further errors",
new Object[] {m_trackingCount, m_ceiling, m_missing}, null );
dumpThreads();
m_missing.clear();
return false;
}
return true;
}
//---------- Component ID management
void registerComponentId()
{
final BundleComponentActivator activator = getActivator();
if ( activator != null )
{
this.m_componentId = activator.registerComponentId( this );
}
}
void unregisterComponentId()
{
if ( this.m_componentId >= 0 )
{
final BundleComponentActivator activator = getActivator();
if ( activator != null )
{
activator.unregisterComponentId( this );
}
this.m_componentId = -1;
}
}
//---------- Asynchronous frontend to state change methods ----------------
private static final AtomicLong taskCounter = new AtomicLong( );
/**
* Enables this component and - if satisfied - also activates it. If
* enabling the component fails for any reason, the component ends up
* disabled.
* <p>
* This method ignores the <i>enabled</i> flag of the component metadata
* and just enables as requested.
* <p>
* This method enables and activates the component immediately.
*/
public final void enable()
{
enable( true );
}
public final void enable( final boolean async )
{
if (m_enabled)
{
return;
}
CountDownLatch enableLatch = null;
try
{
enableLatch = enableLatchWait();
enableInternal();
if ( !async )
{
activateInternal( m_trackingCount.get() );
}
}
finally
{
if ( !async )
{
enableLatch.countDown();
}
m_enabled = true;
}
if ( async )
{
final CountDownLatch latch = enableLatch;
m_activator.schedule( new Runnable()
{
long count = taskCounter.incrementAndGet();
public void run()
{
try
{
activateInternal( m_trackingCount.get() );
}
finally
{
latch.countDown();
}
}
public String toString()
{
return "Async Activate: " + getComponentMetadata().getName() + " id: " + count;
}
} );
}
}
/**
* Use a CountDownLatch as a non-reentrant "lock" that can be passed between threads.
* This lock assures that enable, disable, and reconfigure operations do not overlap.
*
* @return the latch to count down when the operation is complete (in the calling or another thread)
* @throws InterruptedException
*/
CountDownLatch enableLatchWait()
{
CountDownLatch enabledLatch;
CountDownLatch newEnabledLatch;
do
{
enabledLatch = m_enabledLatchRef.get();
boolean waited = false;
boolean interrupted = false;
while ( !waited )
{
try
{
enabledLatch.await();
waited = true;
}
catch ( InterruptedException e )
{
interrupted = true;
}
}
if ( interrupted )
{
Thread.currentThread().interrupt();
}
newEnabledLatch = new CountDownLatch(1);
}
while ( !m_enabledLatchRef.compareAndSet( enabledLatch, newEnabledLatch) );
return newEnabledLatch;
}
/**
* Disables this component and - if active - first deactivates it. The
* component may be reenabled by calling the {@link #enable()} method.
* <p>
* This method deactivates and disables the component immediately.
*/
public final void disable()
{
disable( true );
}
public final void disable( final boolean async )
{
if (!m_enabled)
{
return;
}
CountDownLatch enableLatch = null;
try
{
enableLatch = enableLatchWait();
if ( !async )
{
deactivateInternal( ComponentConstants.DEACTIVATION_REASON_DISABLED, true, false );
}
disableInternal();
}
finally
{
if (!async)
{
enableLatch.countDown();
}
m_enabled = false;
}
if ( async )
{
final CountDownLatch latch = enableLatch;
m_activator.schedule( new Runnable()
{
long count = taskCounter.incrementAndGet();
public void run()
{
try
{
deactivateInternal( ComponentConstants.DEACTIVATION_REASON_DISABLED, true, false );
}
finally
{
latch.countDown();
}
}
public String toString()
{
return "Async Deactivate: " + getComponentMetadata().getName() + " id: " + count;
}
} );
}
}
// supports the ComponentInstance.dispose() method
void dispose()
{
dispose( ComponentConstants.DEACTIVATION_REASON_DISPOSED );
}
/**
* Disposes off this component deactivating and disabling it first as
* required. After disposing off the component, it may not be used anymore.
* <p>
* This method unlike the other state change methods immediately takes
* action and disposes the component. The reason for this is, that this
* method has to actually complete before other actions like bundle stopping
* may continue.
*/
public void dispose( int reason )
{
deactivateInternal( reason, true, true );
}
<T> void registerMissingDependency( DependencyManager<S, T> dm, ServiceReference<T> ref, int trackingCount)
{
BundleComponentActivator activator = getActivator();
if ( activator != null )
{
activator.registerMissingDependency( dm, ref, trackingCount );
}
}
//---------- Component interface ------------------------------------------
public long getId()
{
return m_componentId;
}
public String getName() {
return m_componentMetadata.getName();
}
/**
* Returns the <code>Bundle</code> providing this component. If the
* component as already been disposed off, this method returns
* <code>null</code>.
*/
public Bundle getBundle()
{
final BundleContext context = getBundleContext();
if ( context != null )
{
try
{
return context.getBundle();
}
catch ( IllegalStateException ise )
{
// if the bundle context is not valid any more
}
}
// already disposed off component or bundle context is invalid
return null;
}
BundleContext getBundleContext()
{
final BundleComponentActivator activator = getActivator();
if ( activator != null )
{
return activator.getBundleContext();
}
return null;
}
public String getClassName()
{
return m_componentMetadata.getImplementationClassName();
}
public String getFactory()
{
return m_componentMetadata.getFactoryIdentifier();
}
public Reference[] getReferences()
{
if ( m_dependencyManagers != null && m_dependencyManagers.size() > 0 )
{
return (Reference[]) m_dependencyManagers.toArray(
new Reference[m_dependencyManagers.size()] );
}
return null;
}
public boolean isImmediate()
{
return m_componentMetadata.isImmediate();
}
public boolean isDefaultEnabled()
{
return m_componentMetadata.isEnabled();
}
public String getActivate()
{
return m_componentMetadata.getActivate();
}
public boolean isActivateDeclared()
{
return m_componentMetadata.isActivateDeclared();
}
public String getDeactivate()
{
return m_componentMetadata.getDeactivate();
}
public boolean isDeactivateDeclared()
{
return m_componentMetadata.isDeactivateDeclared();
}
public String getModified()
{
return m_componentMetadata.getModified();
}
public String getConfigurationPolicy()
{
return m_componentMetadata.getConfigurationPolicy();
}
public String getConfigurationPid()
{
return m_componentMetadata.getConfigurationPid();
}
public boolean isConfigurationPidDeclared()
{
return m_componentMetadata.isConfigurationPidDeclared();
}
public boolean isServiceFactory()
{
return m_componentMetadata.getServiceMetadata() != null
&& m_componentMetadata.getServiceMetadata().isServiceFactory();
}
public boolean isFactory()
{
return false;
}
public String[] getServices()
{
if ( m_componentMetadata.getServiceMetadata() != null )
{
return m_componentMetadata.getServiceMetadata().getProvides();
}
return null;
}
//-------------- atomic transition methods -------------------------------
final void enableInternal()
{
if ( m_disposed )
{
throw new IllegalStateException( "enable: " + this );
}
if ( !isActivatorActive() )
{
log( LogService.LOG_DEBUG, "Bundle's component activator is not active; not enabling component",
null );
return;
}
registerComponentId();
// Before creating the implementation object, we are going to
// test if we have configuration if such is required
if ( hasConfiguration() || !getComponentMetadata().isConfigurationRequired() )
{
// Update our target filters.
log( LogService.LOG_DEBUG, "Updating target filters", null );
updateTargets( getProperties() );
}
m_internalEnabled = true;
log( LogService.LOG_DEBUG, "Component enabled", null );
}
final void activateInternal( int trackingCount )
{
log( LogService.LOG_DEBUG, "ActivateInternal",
null );
if ( m_disposed )
{
log( LogService.LOG_DEBUG, "ActivateInternal: disposed",
null );
return;
}
if ( m_activated ) {
log( LogService.LOG_DEBUG, "ActivateInternal: already activated",
null );
return;
}
if ( !isEnabled())
{
log( LogService.LOG_DEBUG, "Component is not enabled; not activating component",
null );
return;
}
if ( !isActivatorActive() )
{
log( LogService.LOG_DEBUG, "Bundle's component activator is not active; not activating component",
null );
return;
}
log( LogService.LOG_DEBUG, "Activating component from state {0}", new Object[] {getState()}, null );
// Before creating the implementation object, we are going to
// test if we have configuration if such is required
if ( !hasConfiguration() && getComponentMetadata().isConfigurationRequired() )
{
log( LogService.LOG_DEBUG, "Missing required configuration, cannot activate", null );
return;
}
// Before creating the implementation object, we are going to
// test that the bundle has enough permissions to register services
if ( !hasServiceRegistrationPermissions() )
{
log( LogService.LOG_DEBUG, "Component is not permitted to register all services, cannot activate",
null );
return;
}
obtainActivationReadLock( "activateInternal" );
try
{
// Before creating the implementation object, we are going to
// test if all the mandatory dependencies are satisfied
if ( !verifyDependencyManagers() )
{
log( LogService.LOG_DEBUG, "Not all dependencies satisfied, cannot activate", null );
return;
}
if ( !registerService() )
{
//some other thread is activating us, or we got concurrently deactivated.
return;
}
if ( ( isImmediate() || getComponentMetadata().isFactory() ) )
{
getServiceInternal();
}
}
finally
{
releaseActivationReadLock( "activateInternal" );
}
}
/**
* Handles deactivating, disabling, and disposing a component manager. Deactivating a factory instance
* always disables and disposes it. Deactivating a factory disposes it.
* @param reason reason for action
* @param disable whether to also disable the manager
* @param dispose whether to also dispose of the manager
*/
final void deactivateInternal( int reason, boolean disable, boolean dispose )
{
synchronized ( this )
{
if ( m_disposed )
{
return;
}
m_disposed = dispose;
}
log( LogService.LOG_DEBUG, "Deactivating component", null );
// catch any problems from deleting the component to prevent the
// component to remain in the deactivating state !
obtainActivationReadLock( "deactivateInternal" );
try
{
doDeactivate( reason, disable || m_factoryInstance );
}
finally
{
releaseActivationReadLock( "deactivateInternal" );
}
if ( isFactory() || m_factoryInstance || dispose )
{
log( LogService.LOG_DEBUG, "Disposing component (reason: " + reason + ")", null );
clear();
}
}
private void doDeactivate( int reason, boolean disable )
{
try
{
if ( !unregisterService() )
{
log( LogService.LOG_DEBUG, "Component deactivation occuring on another thread", null );
}
obtainStateLock( "AbstractComponentManager.State.doDeactivate.1" );
try
{
if ( m_activated )
{
m_activated = false;
deleteComponent( reason );
deactivateDependencyManagers();
if ( disable )
{
disableDependencyManagers();
}
unsetDependenciesCollected();
}
}
finally
{
releaseStateLock( "AbstractComponentManager.State.doDeactivate.1" );
}
}
catch ( Throwable t )
{
log( LogService.LOG_WARNING, "Component deactivation threw an exception", t );
}
}
final void disableInternal()
{
m_internalEnabled = false;
if ( m_disposed )
{
throw new IllegalStateException( "Cannot disable a disposed component " + getName() );
}
unregisterComponentId();
}
final ServiceReference<S> getServiceReference()
{
ServiceRegistration<S> reg = getServiceRegistration();
if (reg != null)
{
return reg.getReference();
}
return null;
}
//---------- Component handling methods ----------------------------------
/**
* Method is called by {@link State#activate(AbstractComponentManager)} in STATE_ACTIVATING or by
* {@link DelayedComponentManager#getService(Bundle, ServiceRegistration)}
* in STATE_REGISTERED.
*
* @return <code>true</code> if creation of the component succeeded. If
* <code>false</code> is returned, the cause should have been logged.
*/
protected abstract boolean createComponent();
protected abstract void deleteComponent( int reason );
boolean getServiceInternal()
{
return false;
}
/**
* All ComponentManagers are ServiceFactory instances
*
* @return this as a ServiceFactory.
*/
private Object getService()
{
return this;
}
ComponentMethods getComponentMethods()
{
return m_componentMethods;
}
protected String[] getProvidedServices()
{
if ( getComponentMetadata().getServiceMetadata() != null )
{
String[] provides = getComponentMetadata().getServiceMetadata().getProvides();
return provides;
}
return null;
}
private final RegistrationManager<ServiceRegistration<S>> registrationManager = new RegistrationManager<ServiceRegistration<S>>()
{
@Override
ServiceRegistration<S> register(String[] services)
{
BundleContext bundleContext = getBundleContext();
if (bundleContext == null)
{
return null;
}
final Dictionary<String, Object> serviceProperties = getServiceProperties();
ServiceRegistration<S> serviceRegistration = ( ServiceRegistration<S> ) bundleContext
.registerService( services, getService(), serviceProperties );
return serviceRegistration;
}
@Override
void unregister(ServiceRegistration<S> serviceRegistration)
{
serviceRegistration.unregister();
}
@Override
void log(int level, String message, Object[] arguments, Throwable ex)
{
AbstractComponentManager.this.log(level, message, arguments, ex);
}
@Override
long getTimeout()
{
return getLockTimeout();
}
@Override
void reportTimeout()
{
dumpThreads();
}
};
/**
* Registers the service on behalf of the component.
*
*/
protected boolean registerService()
{
String[] services = getProvidedServices();
if ( services != null )
{
return registrationManager.changeRegistration( RegistrationManager.RegState.registered, services);
}
return true;
}
protected boolean unregisterService()
{
String[] services = getProvidedServices();
if ( services != null )
{
return registrationManager.changeRegistration( RegistrationManager.RegState.unregistered, services );
}
return true;
}
AtomicInteger getTrackingCount()
{
return m_trackingCount;
}
private void initDependencyManagers()
{
if ( m_dependencyManagersInitialized )
{
return;
}
final Bundle bundle = getBundle();
if (bundle == null)
{
log( LogService.LOG_ERROR, "bundle shut down while trying to load implementation object class", null );
throw new IllegalStateException("bundle shut down while trying to load implementation object class");
}
Class<?> implementationObjectClass;
try
{
implementationObjectClass = bundle.loadClass(
getComponentMetadata().getImplementationClassName() );
}
catch ( ClassNotFoundException e )
{
log( LogService.LOG_ERROR, "Could not load implementation object class {0}",
new Object[] {getComponentMetadata().getImplementationClassName()}, e );
throw new IllegalStateException("Could not load implementation object class "
+ getComponentMetadata().getImplementationClassName());
}
m_componentMethods.initComponentMethods( m_componentMetadata, implementationObjectClass );
for ( DependencyManager dependencyManager : m_dependencyManagers )
{
dependencyManager.initBindingMethods( m_componentMethods.getBindMethods( dependencyManager.getName() ) );
}
m_dependencyManagersInitialized = true;
}
/**
* Collect and store in m_dependencies_map all the services for dependencies, outside of any locks.
* Throwing IllegalStateException on failure to collect all the dependencies is needed so getService can
* know to return null.
*
* @return true if this thread collected the dependencies;
* false if some other thread successfully collected the dependencies;
* @throws IllegalStateException if some dependency is no longer available.
*/
protected boolean collectDependencies() throws IllegalStateException
{
if ( m_dependenciesCollected)
{
log( LogService.LOG_DEBUG, "dependencies already collected, do not collect dependencies", null );
return false;
}
initDependencyManagers();
for ( DependencyManager<S, ?> dependencyManager : m_dependencyManagers )
{
if ( !dependencyManager.prebind() )
{
//not actually satisfied any longer
deactivateDependencyManagers();
log( LogService.LOG_DEBUG, "Could not get required dependency for dependency manager: {0}",
new Object[] {dependencyManager.getName()}, null );
throw new IllegalStateException( "Missing dependencies, not satisfied" );
}
}
m_dependenciesCollected = true;
log( LogService.LOG_DEBUG, "This thread collected dependencies", null );
return true;
}
protected void unsetDependenciesCollected()
{
m_dependenciesCollected = false;
}
abstract <T> void invokeUpdatedMethod( DependencyManager<S, T> dependencyManager, RefPair<T> refPair, int trackingCount );
abstract <T> void invokeBindMethod( DependencyManager<S, T> dependencyManager, RefPair<T> refPair, int trackingCount );
abstract <T> void invokeUnbindMethod( DependencyManager<S, T> dependencyManager, RefPair<T> oldRefPair, int trackingCount );
//**********************************************************************************************************
public BundleComponentActivator getActivator()
{
return m_activator;
}
boolean isActivatorActive()
{
BundleComponentActivator activator = getActivator();
return activator != null && activator.isActive();
}
final ServiceRegistration<S> getServiceRegistration()
{
return registrationManager.getServiceRegistration();
}
synchronized void clear()
{
// for some testing, the activator may be null
if ( m_activator != null )
{
m_activator.unregisterComponentId( this );
}
}
/**
* Returns <code>true</code> if logging for the given level is enabled.
*/
public boolean isLogEnabled( int level )
{
return Activator.isLogEnabled( level );
}
public void log( int level, String message, Throwable ex )
{
BundleComponentActivator activator = getActivator();
if ( activator != null )
{
activator.log( level, message, getComponentMetadata(), m_componentId, ex );
}
}
public void log( int level, String message, Object[] arguments, Throwable ex )
{
BundleComponentActivator activator = getActivator();
if ( activator != null )
{
activator.log( level, message, arguments, getComponentMetadata(), m_componentId, ex );
}
}
public String toString()
{
return "Component: " + getName() + " (" + getId() + ")";
}
private boolean hasServiceRegistrationPermissions()
{
boolean allowed = true;
if ( System.getSecurityManager() != null )
{
final ServiceMetadata serviceMetadata = getComponentMetadata().getServiceMetadata();
if ( serviceMetadata != null )
{
final String[] services = serviceMetadata.getProvides();
if ( services != null && services.length > 0 )
{
final Bundle bundle = getBundle();
for ( String service : services )
{
final Permission perm = new ServicePermission( service, ServicePermission.REGISTER );
if ( !bundle.hasPermission( perm ) )
{
log( LogService.LOG_DEBUG, "Permission to register service {0} is denied", new Object[]
{service}, null );
allowed = false;
}
}
}
}
}
// no security manager or no services to register
return allowed;
}
private List<DependencyManager<S, ?>> loadDependencyManagers( ComponentMetadata metadata )
{
List<DependencyManager<S, ?>> depMgrList = new ArrayList<DependencyManager<S, ?>>(metadata.getDependencies().size());
// If this component has got dependencies, create dependency managers for each one of them.
if ( metadata.getDependencies().size() != 0 )
{
int index = 0;
for ( ReferenceMetadata currentdependency: metadata.getDependencies() )
{
DependencyManager<S, ?> depmanager = new DependencyManager( this, currentdependency, index++ );
depMgrList.add( depmanager );
}
}
return depMgrList;
}
final void updateTargets(Dictionary<String, Object> properties)
{
for ( DependencyManager<S, ?> dm: getDependencyManagers() )
{
dm.setTargetFilter( properties );
}
}
protected boolean verifyDependencyManagers()
{
// indicates whether all dependencies are satisfied
boolean satisfied = true;
for ( DependencyManager<S, ?> dm: getDependencyManagers() )
{
if ( !dm.hasGetPermission() )
{
// bundle has no service get permission
if ( dm.isOptional() )
{
log( LogService.LOG_DEBUG, "No permission to get optional dependency: {0}; assuming satisfied",
new Object[]
{ dm.getName() }, null );
}
else
{
log( LogService.LOG_DEBUG, "No permission to get mandatory dependency: {0}; assuming unsatisfied",
new Object[]
{ dm.getName() }, null );
satisfied = false;
}
}
else if ( !dm.isSatisfied() )
{
// bundle would have permission but there are not enough services
log( LogService.LOG_DEBUG, "Dependency not satisfied: {0}", new Object[]
{ dm.getName() }, null );
satisfied = false;
}
}
return satisfied;
}
/**
* Returns an iterator over the {@link DependencyManager} objects
* representing the declared references in declaration order
*/
List<DependencyManager<S, ?>> getDependencyManagers()
{
return m_dependencyManagers;
}
/**
* Returns an iterator over the {@link DependencyManager} objects
* representing the declared references in reversed declaration order
*/
List<DependencyManager<S, ?>> getReversedDependencyManagers()
{
List<DependencyManager<S, ?>> list = new ArrayList<DependencyManager<S, ?>>( m_dependencyManagers );
Collections.reverse( list );
return list;
}
DependencyManager<S, ?> getDependencyManager(String name)
{
for ( DependencyManager<S, ?> dm: getDependencyManagers() )
{
if ( name.equals(dm.getName()) )
{
return dm;
}
}
// not found
return null;
}
private void deactivateDependencyManagers()
{
log( LogService.LOG_DEBUG, "Deactivating dependency managers", null);
for ( DependencyManager<S, ?> dm: getDependencyManagers() )
{
dm.deactivate();
}
}
private void disableDependencyManagers()
{
log( LogService.LOG_DEBUG, "Disabling dependency managers", null);
AtomicInteger trackingCount = new AtomicInteger();
for ( DependencyManager<S, ?> dm: getDependencyManagers() )
{
dm.unregisterServiceListener( trackingCount );
}
}
public abstract boolean hasConfiguration();
public abstract Dictionary<String, Object> getProperties();
public abstract void setServiceProperties( Dictionary<String, Object> serviceProperties );
/**
* Returns the subset of component properties to be used as service
* properties. These properties are all component properties where property
* name does not start with dot (.), properties which are considered
* private.
*/
public Dictionary<String, Object> getServiceProperties()
{
return copyTo( null, getProperties(), false );
}
/**
* Copies the properties from the <code>source</code> <code>Dictionary</code>
* into the <code>target</code> <code>Dictionary</code>.
*
* @param target The <code>Dictionary</code> into which to copy the
* properties. If <code>null</code> a new <code>Hashtable</code> is
* created.
* @param source The <code>Dictionary</code> providing the properties to
* copy. If <code>null</code> or empty, nothing is copied.
*
* @return The <code>target</code> is returned, which may be empty if
* <code>source</code> is <code>null</code> or empty and
* <code>target</code> was <code>null</code>.
*/
protected static Dictionary<String, Object> copyTo( Dictionary<String, Object> target, Dictionary<String, Object> source )
{
return copyTo( target, source, true );
}
/**
* Copies the properties from the <code>source</code> <code>Dictionary</code>
* into the <code>target</code> <code>Dictionary</code> except for private
* properties (whose name has a leading dot) which are only copied if the
* <code>allProps</code> parameter is <code>true</code>.
*
* @param target The <code>Dictionary</code> into which to copy the
* properties. If <code>null</code> a new <code>Hashtable</code> is
* created.
* @param source The <code>Dictionary</code> providing the properties to
* copy. If <code>null</code> or empty, nothing is copied.
* @param allProps Whether all properties (<code>true</code>) or only the
* public properties (<code>false</code>) are to be copied.
*
* @return The <code>target</code> is returned, which may be empty if
* <code>source</code> is <code>null</code> or empty and
* <code>target</code> was <code>null</code> or all properties are
* private and had not to be copied
*/
protected static Dictionary<String, Object> copyTo( Dictionary<String, Object> target, final Dictionary<String, Object> source, final boolean allProps )
{
if ( target == null )
{
target = new Hashtable<String, Object>();
}
if ( source != null && !source.isEmpty() )
{
for ( Enumeration ce = source.keys(); ce.hasMoreElements(); )
{
// cast is save, because key must be a string as per the spec
String key = ( String ) ce.nextElement();
if ( allProps || key.charAt( 0 ) != '.' )
{
target.put( key, source.get( key ) );
}
}
}
return target;
}
/**
*
*/
public ComponentMetadata getComponentMetadata()
{
return m_componentMetadata;
}
public int getState()
{
if (m_disposed)
{
return Component.STATE_DISPOSED;
}
if ( !m_internalEnabled)
{
return Component.STATE_DISABLED;
}
if ( getServiceRegistration() == null && (getProvidedServices() != null || !hasInstance()))
{
return Component.STATE_UNSATISFIED;
}
if ( isFactory() && !m_factoryInstance )
{
return Component.STATE_FACTORY;
}
if ( hasInstance() )
{
return Component.STATE_ACTIVE;
}
return Component.STATE_REGISTERED;
}
abstract boolean hasInstance();
public void setServiceProperties( MethodResult methodResult )
{
if ( methodResult.hasResult() )
{
Dictionary<String, Object> serviceProps = ( methodResult.getResult() == null) ? null : new Hashtable<String, Object>( methodResult.getResult() );
setServiceProperties(serviceProps );
}
}
boolean isEnabled()
{
return m_internalEnabled;
}
}