blob: af2c65a93a24ec6110a361eaa1a5a85b2f641d9e [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.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
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.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
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.impl.inject.ComponentMethods;
import org.apache.felix.scr.impl.inject.MethodResult;
import org.apache.felix.scr.impl.inject.RefPair;
import org.apache.felix.scr.impl.logger.ComponentLogger;
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.apache.felix.scr.impl.metadata.TargetedPID;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceException;
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.component.runtime.dto.ComponentConfigurationDTO;
import org.osgi.service.log.LogService;
import org.osgi.util.promise.Deferred;
import org.osgi.util.promise.Promise;
/**
* The default ComponentManager. Objects of this class are responsible for managing
* implementation object's lifecycle.
*
*/
public abstract class AbstractComponentManager<S> implements ComponentManager<S>
{
//useful text for deactivation reason numbers
static final String[] REASONS = { "Unspecified", "Component disabled", "Reference became unsatisfied",
"Configuration modified", "Configuration deleted", "Component disabled", "Bundle stopped" };
protected enum State
{
//disposed is a final state, normally only for factory components
disposed(-1, false, false, false),
//Since enable/disable on the component description are asynchronous, this tracks the component configuration state
//which may differ while the enable/disable is occurring.
disabled(-1, false, false, false),
unsatisfiedReference(ComponentConfigurationDTO.UNSATISFIED_REFERENCE, true, false, false),
satisfied(ComponentConfigurationDTO.SATISFIED, true, true, false),
active(ComponentConfigurationDTO.ACTIVE, true, true, true);
private final int specState;
private final boolean enabled;
private final boolean satisfed;
private final boolean actve;
private State(int specState, boolean enabled, boolean satisfied, boolean active)
{
this.specState = specState;
this.enabled = enabled;
this.satisfed = satisfied;
this.actve = active;
}
public int getSpecState()
{
return specState;
}
public boolean isEnabled()
{
return enabled;
}
public boolean isSatisfied()
{
return satisfed;
}
public boolean isActive()
{
return actve;
}
}
protected final ComponentContainer<S> m_container;
//true for normal spec factory instances. False for "persistent" factory instances and obsolete use of factory component with factory configurations.
protected final boolean m_factoryInstance;
// the ID of this component
private volatile long m_componentId;
private final ComponentMethods<S> m_componentMethods;
// The dependency managers that manage every dependency
private final List<DependencyManager<S, ?>> m_dependencyManagers;
private volatile boolean m_dependencyManagersInitialized;
private final AtomicInteger m_trackingCount = new AtomicInteger();
private final ReentrantLock m_stateLock;
/**
* This latch prevents concurrent enable, disable, and reconfigure. Since the enable and disable operations may use
* two threads and the initiating thread does not wait for the operation to complete, we can't use a regular lock.
*/
private final AtomicReference<Deferred<Void>> m_enabledLatchRef = new AtomicReference<>(
new Deferred<Void>());
private final AtomicReference<State> state = new AtomicReference<>(State.disabled);
//service event tracking
private volatile 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<>();
protected final ReentrantReadWriteLock m_activationLock = new ReentrantReadWriteLock();
private volatile String failureReason;
/**
* The constructor receives both the container and the methods.
*
* @param container The component container
* @param componentMethods The component methods
*/
protected AbstractComponentManager(ComponentContainer<S> container, ComponentMethods<S> componentMethods)
{
this(container, componentMethods, false);
}
/**
* The constructor receives both the container and the methods.
*
* @param container The component container
* @param componentMethods The component methods
* @param factoryInstance Flag whether this is a factory instance
*/
protected AbstractComponentManager(ComponentContainer<S> container, ComponentMethods<S> componentMethods, boolean factoryInstance)
{
m_enabledLatchRef.get().resolve(null);
m_factoryInstance = factoryInstance;
m_container = container;
m_componentMethods = componentMethods;
m_componentId = -1;
final ComponentMetadata metadata = container.getComponentMetadata();
m_dependencyManagers = loadDependencyManagers(metadata);
m_stateLock = new ReentrantLock(true);
// dump component details
if (m_container.getLogger().isLogEnabled(LogService.LOG_DEBUG))
{
m_container.getLogger().log(LogService.LOG_DEBUG,
"Component created: DS={0}, implementation={1}, immediate={2}, default-enabled={3}, factory={4}, configuration-policy={5}, activate={6}, deactivate={7}, modified={8} configuration-pid={9}",
null,
metadata.getDSVersion(), metadata.getImplementationClassName(),
metadata.isImmediate(), metadata.isEnabled(), metadata.getFactoryIdentifier(),
metadata.getConfigurationPolicy(), metadata.getActivate(), metadata.getDeactivate(),
metadata.getModified(), metadata.getConfigurationPid());
if (metadata.getServiceMetadata() != null)
{
m_container.getLogger().log(LogService.LOG_DEBUG,
"Component Services: scope={0}, services={1}",
null,
metadata.getServiceScope(), Arrays.toString(metadata.getServiceMetadata().getProvides()));
}
if (metadata.getProperties() != null)
{
m_container.getLogger().log(LogService.LOG_DEBUG, "Component Properties: {0}", null,
metadata.getProperties() );
}
}
}
final long getLockTimeout()
{
//for tests....
if (m_container.getActivator().getConfiguration() != null)
{
return m_container.getActivator().getConfiguration().lockTimeout();
}
return ScrConfiguration.DEFAULT_LOCK_TIMEOUT_MILLISECONDS;
}
private void obtainLock(Lock lock)
{
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();
throw new IllegalStateException("Interrupted twice: Could not obtain lock");
}
Thread.currentThread().interrupt();
}
}
final void obtainActivationReadLock()
{
obtainLock(m_activationLock.readLock());
}
final void releaseActivationReadLock()
{
m_activationLock.readLock().unlock();
}
final void obtainActivationWriteLock()
{
obtainLock(m_activationLock.writeLock());
}
final void releaseActivationWriteeLock()
{
if (m_activationLock.getWriteHoldCount() > 0)
{
m_activationLock.writeLock().unlock();
}
}
final void obtainStateLock()
{
obtainLock(m_stateLock);
}
final void releaseStateLock()
{
m_stateLock.unlock();
}
final boolean isStateLocked()
{
return m_stateLock.getHoldCount() > 0;
}
final void dumpThreads()
{
try
{
String dump = new ThreadDump().call();
m_container.getLogger().log(LogService.LOG_DEBUG, dump, null);
}
catch (Throwable t)
{
m_container.getLogger().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))
{
m_container.getLogger().log(LogService.LOG_DEBUG, "waitForTracked trackingCount: {0} ceiling: {1} missing: {2}", null,
trackingCount, m_ceiling, m_missing );
try
{
if (!doMissingWait())
{
return;
}
}
catch (InterruptedException e)
{
try
{
if (!doMissingWait())
{
return;
}
}
catch (InterruptedException e1)
{
m_container.getLogger().log(LogService.LOG_ERROR,
"waitForTracked interrupted twice: {0} ceiling: {1} missing: {2}, Expect further errors", e1,
trackingCount, m_ceiling, m_missing );
}
Thread.currentThread().interrupt();
}
}
}
finally
{
m_missingLock.unlock();
}
}
private boolean doMissingWait() throws InterruptedException
{
if (!m_missingCondition.await(getLockTimeout(), TimeUnit.MILLISECONDS))
{
m_container.getLogger().log(LogService.LOG_ERROR, "waitForTracked timed out: {0} ceiling: {1} missing: {2}, Expect further errors", null,
m_trackingCount, m_ceiling, m_missing);
dumpThreads();
m_missing.clear();
return false;
}
return true;
}
//---------- Component ID management
void registerComponentId()
{
this.m_componentId = m_container.getActivator().registerComponentId(this);
this.m_container.getLogger().setComponentId(this.m_componentId);
}
void unregisterComponentId()
{
if (this.m_componentId >= 0)
{
m_container.getActivator().unregisterComponentId(this);
this.m_componentId = -1;
this.m_container.getLogger().setComponentId(this.m_componentId);
}
}
//---------- Asynchronous frontend to state change methods ----------------
private static final AtomicLong taskCounter = new AtomicLong();
public final Promise<Void> enable(final boolean async)
{
Deferred<Void> enableLatch = null;
try
{
enableLatch = enableLatchWait();
if (!async)
{
enableInternal();
}
}
finally
{
if (!async)
{
enableLatch.resolve(null);
}
}
if (async)
{
final Deferred<Void> latch = enableLatch;
m_container.getActivator().schedule(new Runnable()
{
long count = taskCounter.incrementAndGet();
@Override
public void run()
{
try
{
enableInternal();
}
finally
{
latch.resolve(null);
}
}
@Override
public String toString()
{
return "Async Activate: " + getComponentMetadata().getName() + " id: " + count;
}
});
}
return enableLatch.getPromise();
}
/**
* 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
*/
Deferred<Void> enableLatchWait()
{
Deferred<Void> enabledLatch;
Deferred<Void> newEnabledLatch;
do
{
enabledLatch = m_enabledLatchRef.get();
boolean waited = false;
boolean interrupted = false;
while (!waited)
{
try
{
enabledLatch.getPromise().getValue();
waited = true;
}
catch (InterruptedException e)
{
interrupted = true;
}
catch (InvocationTargetException e)
{
//this is not going to happen
}
}
if (interrupted)
{
Thread.currentThread().interrupt();
}
newEnabledLatch = new Deferred<>();
}
while (!m_enabledLatchRef.compareAndSet(enabledLatch, newEnabledLatch));
return newEnabledLatch;
}
public final Promise<Void> disable(final boolean async)
{
Deferred<Void> enableLatch = null;
try
{
enableLatch = enableLatchWait();
if (!async)
{
disableInternal();
}
}
finally
{
if (!async)
{
enableLatch.resolve(null);
}
}
if (async)
{
final Deferred<Void> latch = enableLatch;
m_container.getActivator().schedule(new Runnable()
{
long count = taskCounter.incrementAndGet();
@Override
public void run()
{
try
{
disableInternal();
}
finally
{
latch.resolve(null);
}
}
@Override
public String toString()
{
return "Async Deactivate: " + getComponentMetadata().getName() + " id: " + count;
}
});
}
return enableLatch.getPromise();
}
// 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)
{
m_container.getActivator().registerMissingDependency(dm, ref, trackingCount);
}
//---------- Component interface ------------------------------------------
@Override
public long getId()
{
return m_componentId;
}
/**
* 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()
{
return m_container.getActivator().getBundleContext();
}
protected boolean isImmediate()
{
return getComponentMetadata().isImmediate();
}
public boolean isFactory()
{
return false;
}
//-------------- atomic transition methods -------------------------------
final void enableInternal()
{
State previousState;
if ((previousState = getState()) == State.disposed)
{
throw new IllegalStateException("enable: " + this);
}
if (!m_container.getActivator().isActive())
{
m_container.getLogger().log(LogService.LOG_DEBUG, "Bundle's component activator is not active; not enabling component", null);
return;
}
if (previousState.isEnabled())
{
m_container.getLogger().log(LogService.LOG_WARNING, "enable called but component is already in state {0}", null,
previousState);
return;
}
registerComponentId();
m_container.getLogger().log(LogService.LOG_DEBUG, "Updating target filters", null);
updateTargets(getProperties());
setState(previousState, State.unsatisfiedReference);
m_container.getLogger().log(LogService.LOG_DEBUG, "Component enabled", null);
activateInternal();
}
final void activateInternal()
{
m_container.getLogger().log(LogService.LOG_DEBUG, "ActivateInternal", null);
State s = getState();
if (s == State.disposed)
{
m_container.getLogger().log(LogService.LOG_DEBUG, "ActivateInternal: disposed", null);
return;
}
if (s == State.active)
{
m_container.getLogger().log(LogService.LOG_DEBUG, "ActivateInternal: already activated", null);
return;
}
if (!s.isEnabled())
{
m_container.getLogger().log(LogService.LOG_DEBUG, "Component is not enabled; not activating component", null);
return;
}
if (!m_container.getActivator().isActive())
{
m_container.getLogger().log(LogService.LOG_DEBUG, "Bundle's component activator is not active; not activating component", null);
return;
}
m_container.getLogger().log(LogService.LOG_DEBUG, "Activating component from state {0}", null, getState() );
// Before creating the implementation object, we are going to
// test that the bundle has enough permissions to register services
if (!hasServiceRegistrationPermissions())
{
m_container.getLogger().log(LogService.LOG_DEBUG, "Component is not permitted to register all services, cannot activate", null);
return;
}
obtainActivationReadLock();
try
{
// Double check conditions now that we have obtained the lock
s = getState();
if (s == State.disposed)
{
m_container.getLogger().log(LogService.LOG_DEBUG, "ActivateInternal: disposed", null);
return;
}
if (s == State.active)
{
m_container.getLogger().log(LogService.LOG_DEBUG, "ActivateInternal: already activated", null);
return;
}
if (!s.isEnabled())
{
m_container.getLogger().log(LogService.LOG_DEBUG, "Component is not enabled; not activating component", null);
return;
}
// Before creating the implementation object, we are going to
// test if all the mandatory dependencies are satisfied
if (!verifyDependencyManagers())
{
m_container.getLogger().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()))
{
final ServiceRegistration<S> serviceRegistration = registrationManager.getServiceRegistration();
ServiceReference<S> ref = null;
try
{
ref = serviceRegistration == null ? null : serviceRegistration.getReference();
}
catch ( final IllegalStateException ise )
{
// catch service already being unregistered again
}
if ( ref != null )
{
m_container.getActivator().enterCreate( ref );
try
{
getServiceInternal( serviceRegistration );
}
finally
{
m_container.getActivator().leaveCreate( ref );
}
}
else
{
getServiceInternal( null );
}
}
}
finally
{
releaseActivationReadLock();
}
}
/**
* 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)
{
if (!getState().isEnabled())
{
return;
}
State nextState = State.unsatisfiedReference;
if (disable)
{
nextState = State.disabled;
}
if (dispose)
{
nextState = State.disposed;
}
m_container.getLogger().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();
try
{
//doDeactivate may trigger a state change from active to satisfied as the registration is removed.
doDeactivate(reason, disable || m_factoryInstance);
setState(getState(), nextState);
}
finally
{
releaseActivationReadLock();
}
if (isFactory() || m_factoryInstance || dispose)
{
m_container.getLogger().log(LogService.LOG_DEBUG, "Disposing component (reason: " + reason + ")", null);
clear();
}
}
private void doDeactivate(int reason, boolean disable)
{
try
{
if (!unregisterService())
{
m_container.getLogger().log(LogService.LOG_DEBUG, "Component deactivation occuring on another thread", null);
}
obtainStateLock();
try
{
// setState(previousState, State.unsatisfiedReference);
deleteComponent(reason);
deactivateDependencyManagers();
if (disable)
{
disableDependencyManagers();
}
}
finally
{
releaseStateLock();
}
}
catch (Throwable t)
{
m_container.getLogger().log(LogService.LOG_WARNING, "Component deactivation threw an exception", t);
}
}
final void disableInternal()
{
deactivateInternal(ComponentConstants.DEACTIVATION_REASON_DISABLED, true, false);
unregisterComponentId();
}
//---------- Component handling methods ----------------------------------
protected abstract void deleteComponent(int reason);
boolean getServiceInternal(ServiceRegistration<S> serviceRegistration)
{
return false;
}
/**
* All ComponentManagers are ServiceFactory instances
*
* @return this as a ServiceFactory.
*/
private Object getService()
{
return this;
}
ComponentMethods<S> getComponentMethods()
{
return m_componentMethods;
}
protected String[] getProvidedServices()
{
if (getComponentMetadata().getServiceMetadata() != null)
{
String[] provides = getComponentMetadata().getServiceMetadata().getProvides();
return provides;
}
return null;
}
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();
try
{
@SuppressWarnings("unchecked")
ServiceRegistration<S> serviceRegistration = (ServiceRegistration<S>) bundleContext.registerService(
services, getService(), serviceProperties);
return serviceRegistration;
}
catch (ServiceException e)
{
log(LogService.LOG_ERROR, "Unexpected error registering component service with properties {0}", e, serviceProperties);
return null;
}
}
@Override
void postRegister(ServiceRegistration<S> t)
{
AbstractComponentManager.this.postRegister();
}
@Override
void unregister(ServiceRegistration<S> serviceRegistration)
{
AbstractComponentManager.this.preDeregister();
serviceRegistration.unregister();
}
@Override
void log(int level, String message, Throwable ex, Object... arguments)
{
AbstractComponentManager.this.getLogger().log(level, message, ex, arguments);
}
@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;
}
protected ServiceRegistration<S> getServiceRegistration()
{
return registrationManager.getServiceRegistration();
}
AtomicInteger getTrackingCount()
{
return m_trackingCount;
}
private void initDependencyManagers(final ComponentContextImpl<S> componentContext)
{
if (m_dependencyManagersInitialized)
{
return;
}
final Bundle bundle = getBundle();
if (bundle == null)
{
m_container.getLogger().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<S> implementationObjectClass;
try
{
implementationObjectClass = (Class<S>) bundle.loadClass(getComponentMetadata().getImplementationClassName());
}
catch (ClassNotFoundException e)
{
m_container.getLogger().log(LogService.LOG_ERROR, "Could not load implementation object class {0}",
e, getComponentMetadata().getImplementationClassName() );
throw new IllegalStateException(
"Could not load implementation object class " + getComponentMetadata().getImplementationClassName());
}
m_componentMethods.initComponentMethods(getComponentMetadata(), implementationObjectClass, componentContext.getLogger());
for (DependencyManager<S, ?> 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.
* @param componentContext possible instance key for prototype scope references
*
* @return true if all references can be collected,
* false if some dependency is no longer available.
*/
protected boolean collectDependencies(ComponentContextImpl<S> componentContext)
{
initDependencyManagers(componentContext);
for (DependencyManager<S, ?> dependencyManager : m_dependencyManagers)
{
if (!dependencyManager.prebind(componentContext))
{
//not actually satisfied any longer
deactivateDependencyManagers();
m_container.getLogger().log(LogService.LOG_DEBUG, "Could not get required dependency for dependency manager: {0}", null,
dependencyManager.getName());
return false;
}
}
m_container.getLogger().log(LogService.LOG_DEBUG, "This thread collected dependencies", null);
return true;
}
/**
* Invoke updated method
* @return {@code true} if the component needs reactivation, {@code false} otherwise.
*/
abstract <T> boolean invokeUpdatedMethod(DependencyManager<S, T> dependencyManager, RefPair<S, T> refPair,
int trackingCount);
abstract <T> void invokeBindMethod(DependencyManager<S, T> dependencyManager, RefPair<S, T> refPair,
int trackingCount);
abstract <T> void invokeUnbindMethod(DependencyManager<S, T> dependencyManager, RefPair<S, T> oldRefPair,
int trackingCount);
void notifyWaiters()
{
if ( registrationManager.getServiceRegistration() != null )
{
//see if our service has been requested but returned null....
m_container.getLogger().log( LogService.LOG_DEBUG, "Notifying possible clients that service might be available with activator {0}", null,
m_container.getActivator() );
try
{
m_container.getActivator().missingServicePresent( registrationManager.getServiceRegistration().getReference() );
}
catch ( final IllegalStateException ise)
{
// service has been unregistered
}
}
}
//**********************************************************************************************************
public ComponentActivator getActivator()
{
return m_container.getActivator();
}
synchronized void clear()
{
m_container.getActivator().unregisterComponentId(this);
}
public ComponentLogger getLogger() {
return m_container.getLogger();
}
@Override
public String toString()
{
return "Component: " + getComponentMetadata().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))
{
m_container.getLogger().log(LogService.LOG_DEBUG, "Permission to register service {0} is denied", null,
service );
allowed = false;
}
}
}
}
}
// no security manager or no services to register
return allowed;
}
private List<DependencyManager<S, ?>> loadDependencyManagers(final ComponentMetadata metadata)
{
// If this component has got dependencies, create dependency managers for each one of them.
if (!metadata.getDependencies().isEmpty())
{
final List<DependencyManager<S, ?>> depMgrList = new ArrayList<>(metadata.getDependencies().size());
int index = 0;
for (final ReferenceMetadata currentdependency : metadata.getDependencies())
{
@SuppressWarnings({ "unchecked", "rawtypes" })
final DependencyManager<S, ?> depmanager = new DependencyManager(this, currentdependency, index++);
depMgrList.add(depmanager);
}
return depMgrList;
}
return Collections.emptyList();
}
final void updateTargets(final Map<String, Object> properties)
{
for (final DependencyManager<S, ?> dm : getDependencyManagers())
{
dm.setTargetFilter(properties);
}
}
protected boolean verifyDependencyManagers()
{
State previousState = getState();
// 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())
{
m_container.getLogger().log(LogService.LOG_DEBUG, "No permission to get optional dependency: {0}; assuming satisfied",
null, dm.getName() );
}
else
{
m_container.getLogger().log(LogService.LOG_DEBUG, "No permission to get mandatory dependency: {0}; assuming unsatisfied",
null, dm.getName() );
satisfied = false;
}
}
else if (!dm.isSatisfied())
{
// bundle would have permission but there are not enough services
m_container.getLogger().log(LogService.LOG_DEBUG, "Dependency not satisfied: {0}", null, dm.getName() );
satisfied = false;
}
}
//Only try to change the state if the satisfied attribute is different.
//We only succeed if no one else has changed the state meanwhile.
if (satisfied != previousState.isSatisfied())
{
setState(previousState, satisfied ? State.satisfied : State.unsatisfiedReference);
}
return satisfied;
}
/**
* Returns an iterator over the {@link DependencyManager} objects
* representing the declared references in declaration order
*/
List<DependencyManager<S, ?>> getDependencyManagers()
{
return m_dependencyManagers;
}
@Override
public List<? extends ReferenceManager<S, ?>> getReferenceManagers()
{
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<>(m_dependencyManagers);
Collections.reverse(list);
return list;
}
DependencyManager<S, ?> getDependencyManager(String name)
{
for (ReferenceManager<S, ?> dm : getDependencyManagers())
{
if (name.equals(dm.getName()))
{
return (DependencyManager<S, ?>) dm;
}
}
// not found
return null;
}
private void deactivateDependencyManagers()
{
m_container.getLogger().log(LogService.LOG_DEBUG, "Deactivating dependency managers", null);
for (DependencyManager<S, ?> dm : getDependencyManagers())
{
dm.deactivate();
}
}
private void disableDependencyManagers()
{
m_container.getLogger().log(LogService.LOG_DEBUG, "Disabling dependency managers", null);
AtomicInteger trackingCount = new AtomicInteger();
for (DependencyManager<S, ?> dm : getDependencyManagers())
{
dm.unregisterServiceListener(trackingCount);
}
}
/* (non-Javadoc)
* @see org.apache.felix.scr.impl.manager.ComponentManager#getProperties()
*/
@Override
public abstract Map<String, Object> getProperties();
public abstract void setServiceProperties(Dictionary<String, ?> 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> 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 Map<String, ?> source,
final boolean allProps)
{
if (target == null)
{
target = new Hashtable<>();
}
if (source != null && !source.isEmpty())
{
for (Map.Entry<String, ?> entry : source.entrySet())
{
// cast is save, because key must be a string as per the spec
String key = entry.getKey();
if (allProps || key.charAt(0) != '.')
{
target.put(key, entry.getValue());
}
}
}
return target;
}
/**
* 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 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 Map<String, Object> copyToMap(final Dictionary<String, ?> source, final boolean allProps)
{
Map<String, Object> target = new HashMap<>();
if (source != null && !source.isEmpty())
{
for (Enumeration<String> ce = source.keys(); ce.hasMoreElements();)
{
// cast is save, because key must be a string as per the spec
String key = ce.nextElement();
if (allProps || key.charAt(0) != '.')
{
target.put(key, source.get(key));
}
}
}
return target;
}
protected static Dictionary<String, Object> copyToDictionary(final Dictionary<String, ?> source,
final boolean allProps)
{
Hashtable<String, Object> target = new Hashtable<>();
if (source != null && !source.isEmpty())
{
for (Enumeration<String> ce = source.keys(); ce.hasMoreElements();)
{
// cast is save, because key must be a string as per the spec
String key = ce.nextElement();
if (allProps || key.charAt(0) != '.')
{
target.put(key, source.get(key));
}
}
}
return target;
}
/**
*
*/
public ComponentMetadata getComponentMetadata()
{
return m_container.getComponentMetadata();
}
@Override
public int getSpecState()
{
return getState().getSpecState();
}
State getState()
{
State s = state.get();
m_container.getLogger().log(LogService.LOG_DEBUG, "Querying state {0}", null, s );
return s;
}
@Override
public String getFailureReason() {
return this.failureReason;
}
/**
* Set the activation failure reason
* @param e The exception which caused the activation to fail
*/
public void setFailureReason(final Throwable e)
{
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
pw.flush();
this.failureReason = sw.toString();
}
void setState(final State previousState, final State newState)
{
if (state.compareAndSet(previousState, newState))
{
m_container.getLogger().log(LogService.LOG_DEBUG, "Changed state from {0} to {1}", null, previousState, newState );
if ( newState == State.active || newState == State.unsatisfiedReference )
{
this.failureReason = null;
}
m_container.getActivator().updateChangeCount();
}
else
{
m_container.getLogger().log(LogService.LOG_DEBUG, "Did not change state from {0} to {1}: current state {2}",
null, previousState, newState, state.get() );
}
}
abstract boolean hasInstance();
public void setServiceProperties(MethodResult methodResult, Integer trackingCount)
{
if (methodResult.hasResult())
{
if (trackingCount != null)
{
tracked(trackingCount);
}
Dictionary<String, Object> serviceProps = (methodResult.getResult() == null) ? null
: new Hashtable<>(methodResult.getResult());
setServiceProperties(serviceProps);
}
}
abstract void postRegister();
abstract void preDeregister();
public abstract void reconfigure(Map<String, Object> configuration, boolean configurationDeleted, TargetedPID factoryPid);
public abstract void getComponentManagers(List<AbstractComponentManager<S>> cms);
@Override
public final ServiceReference<S> getRegisteredServiceReference()
{
final ServiceRegistration<S> reg = registrationManager.getServiceRegistration();
if ( reg != null )
{
try
{
return reg.getReference();
}
catch ( final IllegalStateException ise)
{
// service has just been unregistered, return null
}
}
return null;
}
}