blob: 7030bc14a4171529faa79726a96de6540dfde94e [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.util.ArrayList;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.felix.scr.impl.inject.ComponentMethods;
import org.apache.felix.scr.impl.inject.LifecycleMethod;
import org.apache.felix.scr.impl.inject.MethodResult;
import org.apache.felix.scr.impl.inject.OpenStatus;
import org.apache.felix.scr.impl.inject.RefPair;
import org.apache.felix.scr.impl.inject.ReferenceMethod;
import org.apache.felix.scr.impl.metadata.DSVersion;
import org.apache.felix.scr.impl.metadata.ReferenceMetadata;
import org.apache.felix.scr.impl.metadata.TargetedPID;
import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.ComponentConstants;
import org.osgi.service.component.ComponentInstance;
import org.osgi.service.log.LogService;
import org.osgi.util.promise.Deferred;
/**
* The default ComponentManager. Objects of this class are responsible for managing
* implementation object's lifecycle.
*/
public class SingleComponentManager<S> extends AbstractComponentManager<S> implements ServiceFactory<S>
{
// keep the using bundles as reference "counters" for instance deactivation
private final AtomicInteger m_useCount = new AtomicInteger( );
// The context that will be passed to the implementationObject
private volatile ComponentContextImpl<S> m_componentContext;
// Merged properties from xml descriptor and all configurations
private Map<String, Object> m_configurationProperties;
// optional properties provided in the ComponentFactory.newInstance method
private Map<String, Object> m_factoryProperties;
// the component properties, also used as service properties
private Map<String, Object> m_properties;
// properties supplied ot ExtComponentContext.updateProperties
// null if properties are not to be overwritten
private Dictionary<String, Object> m_serviceProperties;
/**
* The constructor receives both the activator and the metadata
* @param componentMethods
*/
public SingleComponentManager( final ComponentContainer<S> container, final ComponentMethods<S> componentMethods )
{
this(container, componentMethods, false);
}
public SingleComponentManager( final ComponentContainer<S> container, final ComponentMethods<S> componentMethods,
final boolean factoryInstance )
{
super( container, componentMethods, factoryInstance );
}
@Override
void clear()
{
m_container.disposed( this );
super.clear();
}
// 1. Load the component implementation class
// 2. Create the component instance and component context
// 3. Bind the target services
// 4. Call the activate method, if present
// if this method is overwritten, the deleteComponent method should
// also be overwritten
private boolean createComponent(ComponentContextImpl<S> componentContext)
{
if ( !isStateLocked() )
{
throw new IllegalStateException( "need write lock (createComponent)" );
}
if ( m_componentContext == null )
{
S tmpComponent = createImplementationObject( null, new SetImplementationObject<S>()
{
@Override
public void presetComponentContext( ComponentContextImpl<S> componentContext )
{
m_componentContext = componentContext;
}
@Override
public void resetImplementationObject( S implementationObject )
{
m_componentContext = null;
}
}, componentContext );
// if something failed creating the component instance, return false
if ( tmpComponent == null )
{
return false;
}
// otherwise set the context and component instance and return true
getLogger().log( LogService.LOG_DEBUG, "Set implementation object for component", null );
//notify that component was successfully created so any optional circular dependencies can be retried
try
{
m_container.getActivator().missingServicePresent( getServiceReference() );
}
catch ( final IllegalStateException ise )
{
return false;
}
}
return true;
}
@Override
protected void deleteComponent( final int reason )
{
if ( !isStateLocked() )
{
throw new IllegalStateException( "need write lock (deleteComponent)" );
}
if ( m_componentContext != null )
{
m_useCount.set( 0 );
disposeImplementationObject( m_componentContext, reason );
m_componentContext = null;
getLogger().log( LogService.LOG_DEBUG, "Unset and deconfigured implementation object for component in deleteComponent for reason {0}", null, REASONS[ reason ] );
clearServiceProperties();
}
}
void clearServiceProperties()
{
m_properties = null;
m_serviceProperties = null;
}
public ComponentInstance<S> getComponentInstance()
{
return m_componentContext == null? null: m_componentContext.getComponentInstance();
}
//**********************************************************************************************************
/**
* Get the object that is implementing this descriptor
*
* @return the object that implements the services
*/
private S getInstance()
{
return m_componentContext == null? null: m_componentContext.getImplementationObject( true );
}
/**
* The <code>SetImplementationObject</code> interface provides an
* API for component managers to setup the implementation object and
* potentially other parts as part of the {@link #createImplementationObject} method
* processing.
*/
protected interface SetImplementationObject<S>
{
/**
* Presets the implementation object. This method is called before
* the component's activator method is called and is intended to
* temporarily set the implementation object during the activator
* call.
*/
void presetComponentContext( ComponentContextImpl<S> componentContext );
/**
* Resets the implementation object. This method is called after
* the activator method terminates with an error and is intended to
* revert any temporary settings done in the {@link #presetComponentContext(ComponentContextImpl)}
* method.
*/
void resetImplementationObject( S implementationObject );
}
@SuppressWarnings("unchecked")
protected S createImplementationObject( Bundle usingBundle, SetImplementationObject<S> setter, ComponentContextImpl<S> componentContext )
{
S implementationObject = null;
// 1. Load the component implementation class
// 2. Create the component instance and component context
// If the component is not immediate, this is not done at this moment
Bundle bundle = getBundle();
if (bundle == null)
{
getLogger().log( LogService.LOG_WARNING, "Bundle shut down during instantiation of the implementation object", null);
return null;
}
// bind target services
final List<OpenStatus<S, ?>> openStatusList = new ArrayList<>();
final Map<ReferenceMetadata, OpenStatus<S, ?>> paramMap = (getComponentMetadata()
.getNumberOfConstructorParameters() > 0 ? new HashMap<ReferenceMetadata, OpenStatus<S, ?>>() : null);
boolean failed = false;
for ( DependencyManager<S, ?> dm : getDependencyManagers())
{
// if a dependency turned unresolved since the validation check,
// creating the instance fails here, so we deactivate and return
// null.
OpenStatus<S, ?> open = dm.open(componentContext, componentContext.getEdgeInfo(dm));
if ( open == null )
{
getLogger().log( LogService.LOG_DEBUG, "Cannot create component instance due to failure to bind reference {0}",
null, dm.getName() );
failed = true;
break;
}
openStatusList.add(open);
if ( dm.getReferenceMetadata().getParameterIndex() != null)
{
if ( !dm.bindDependency(componentContext, ReferenceMethod.NOPReferenceMethod, (OpenStatus) open) )
{
getLogger().log( LogService.LOG_DEBUG, "Cannot create component instance due to failure to bind reference {0}",
null, dm.getName() );
failed = true;
break;
}
paramMap.put(dm.getReferenceMetadata(), open);
}
}
if ( !failed )
{
try
{
implementationObject = getComponentMethods().getConstructor().newInstance(
componentContext,
paramMap);
}
catch ( final InstantiationException ie)
{
// we don't need to log the stack trace
getLogger().log( LogService.LOG_ERROR, "Error during instantiation of the implementation object: " + ie.getMessage(), null );
this.setFailureReason(ie);
return null;
}
catch ( final Throwable t )
{
// failed to instantiate, return null
getLogger().log( LogService.LOG_ERROR, "Error during instantiation of the implementation object", t );
this.setFailureReason(t);
return null;
}
componentContext.setImplementationObject(implementationObject);
// 3. set the implementation object prematurely
setter.presetComponentContext( componentContext );
// 4. Bind the target services
final Iterator<OpenStatus<S, ?>> iter = openStatusList.iterator();
for ( DependencyManager<S, ?> dm: getDependencyManagers())
{
final OpenStatus<S, ?> open = iter.next();
if ( !dm.bind(componentContext, (OpenStatus) open) )
{
getLogger().log( LogService.LOG_DEBUG, "Cannot create component instance due to failure to bind reference {0}",
null, dm.getName() );
failed = true;
break;
}
}
}
if (failed)
{
// make sure, we keep no bindings. Only close the dm's we opened.
int skipCount = getReversedDependencyManagers().size() - openStatusList.size();
for ( DependencyManager<S, ?> md: getReversedDependencyManagers() )
{
if ( skipCount > 0 )
{
skipCount--;
}
else
{
md.close( componentContext, componentContext.getEdgeInfo( md ) );
}
md.deactivate();
}
setter.resetImplementationObject( implementationObject );
return null;
}
// 5. Call the activate method, if present
final MethodResult failedResult = new MethodResult(true, new HashMap<String, Object>());
final MethodResult result = getComponentMethods().getActivateMethod().invoke( implementationObject,
componentContext, 1, failedResult );
if ( result == failedResult )
{
this.setFailureReason((Throwable)failedResult.getResult().get("exception"));
// 112.5.8 If the activate method throws an exception, SCR must log an error message
// containing the exception with the Log Service and activation fails
for ( DependencyManager<S, ?> md: getReversedDependencyManagers() )
{
md.close( componentContext, componentContext.getEdgeInfo( md ) );
}
if ( implementationObject != null )
{
// make sure the implementation object is not available
setter.resetImplementationObject( implementationObject );
}
return null;
}
else
{
componentContext.setImplementationAccessible( true );
//call to leaveCreate must be done here since the change in service properties may cause a getService,
//so the threadLocal must be cleared first.
try
{
m_container.getActivator().leaveCreate(getServiceReference());
}
catch ( final IllegalStateException ise)
{
// already unregistered again
this.setFailureReason(ise);
for ( DependencyManager<S, ?> md: getReversedDependencyManagers() )
{
md.close( componentContext, componentContext.getEdgeInfo( md ) );
}
if ( implementationObject != null )
{
// make sure the implementation object is not available
setter.resetImplementationObject( implementationObject );
}
return null;
}
//this may cause a getService as properties now match a filter.
setServiceProperties( result, null );
}
return implementationObject;
}
protected void disposeImplementationObject( ComponentContextImpl<S> componentContext,
int reason )
{
componentContext.setImplementationAccessible( false );
S implementationObject = componentContext.getImplementationObject( false );
if ( implementationObject != null )
{
// 1. Call the deactivate method, if present
// don't care for the result, the error (acccording to 112.5.12 If the deactivate
// method throws an exception, SCR must log an error message containing the
// exception with the Log Service and continue) has already been logged
final MethodResult result = getComponentMethods().getDeactivateMethod().invoke( implementationObject,
componentContext, reason, null );
if ( result != null )
{
setServiceProperties( result, null );
}
// 2. Unbind any bound services
for ( DependencyManager<S, ?> md: getReversedDependencyManagers() )
{
md.close( componentContext, componentContext.getEdgeInfo( md ) );
}
}
componentContext.cleanup();
}
@Override
boolean hasInstance()
{
return m_componentContext != null;
}
@Override
<T> void invokeBindMethod( DependencyManager<S, T> dependencyManager, RefPair<S, T> refPair, int trackingCount )
{
ComponentContextImpl<S> componentContext = m_componentContext;
if ( componentContext != null )
{
EdgeInfo info = componentContext.getEdgeInfo( dependencyManager );
dependencyManager.invokeBindMethod( componentContext, refPair, trackingCount, info );
}
}
@Override
<T> boolean invokeUpdatedMethod( DependencyManager<S, T> dependencyManager, RefPair<S, T> refPair, int trackingCount )
{
final ComponentContextImpl<S> componentContext = m_componentContext;
if ( componentContext != null )
{
final EdgeInfo info = componentContext.getEdgeInfo( dependencyManager );
return dependencyManager.invokeUpdatedMethod( componentContext, refPair, trackingCount, info );
}
return false;
}
@Override
<T> void invokeUnbindMethod( DependencyManager<S, T> dependencyManager, RefPair<S, T> oldRefPair, int trackingCount )
{
ComponentContextImpl<S> componentContext = m_componentContext;
if ( componentContext != null )
{
EdgeInfo info = componentContext.getEdgeInfo( dependencyManager );
dependencyManager.invokeUnbindMethod( componentContext, oldRefPair, trackingCount, info );
}
}
protected void setFactoryProperties( Dictionary<String, ?> dictionary )
{
m_factoryProperties = copyToMap( dictionary, true );
}
@Override
void registerComponentId()
{
super.registerComponentId();
this.m_properties = null;
}
@Override
void unregisterComponentId()
{
super.unregisterComponentId();
this.m_properties = null;
}
/**
* Returns the (private copy) of the Component properties to be used
* for the ComponentContext as well as eventual service registration.
* <p/>
* Method implements the Component Properties provisioning as described
* in 112.6, Component Properties.
*
* @return a private map of component properties
*/
@Override
public Map<String, Object> getProperties()
{
if ( m_properties == null )
{
// 1. Merge all the config properties
Map<String, Object> props = new HashMap<>();
if ( m_configurationProperties != null )
{
props.putAll(m_configurationProperties);
}
if ( m_factoryProperties != null)
{
props.putAll(m_factoryProperties);
if (getComponentMetadata().getDSVersion().isDS13() && m_factoryProperties.containsKey(Constants.SERVICE_PID))
{
final List<String> servicePids = new ArrayList<>();
final Object configPropServicePids = m_configurationProperties.get(Constants.SERVICE_PID);
if ( configPropServicePids instanceof List )
{
servicePids.addAll((List)configPropServicePids);
}
else
{
servicePids.add(configPropServicePids.toString());
}
if (m_factoryProperties.get(Constants.SERVICE_PID) instanceof String)
{
servicePids.add((String)m_factoryProperties.get(Constants.SERVICE_PID));
}
if ( servicePids.size() == 1 )
{
props.put(Constants.SERVICE_PID, servicePids.get(0));
}
else
{
props.put(Constants.SERVICE_PID, servicePids);
}
}
}
// 2. set component.name and component.id
props.put( ComponentConstants.COMPONENT_NAME, getComponentMetadata().getName() );
props.put( ComponentConstants.COMPONENT_ID, getId() );
m_properties = props;
}
return m_properties;
}
@Override
public void setServiceProperties( Dictionary<String, ?> serviceProperties )
{
if ( serviceProperties == null || serviceProperties.isEmpty() )
{
m_serviceProperties = null;
}
else
{
m_serviceProperties = copyToDictionary( serviceProperties, false );
// set component.name and component.id
m_serviceProperties.put( ComponentConstants.COMPONENT_NAME, getComponentMetadata().getName() );
m_serviceProperties.put( ComponentConstants.COMPONENT_ID, getId() );
}
updateServiceRegistration();
}
@Override
void postRegister()
{
if (m_serviceProperties != null)
{
updateServiceRegistration();
}
}
@Override
void preDeregister()
{
if (m_componentContext != null)
{
m_componentContext.unsetServiceRegistration();
}
}
@Override
public Dictionary<String, Object> getServiceProperties()
{
if ( m_serviceProperties != null )
{
return m_serviceProperties;
}
return super.getServiceProperties();
}
final ServiceReference<S> getServiceReference()
{
ServiceRegistration<S> reg = getServiceRegistration();
if (reg != null)
{
return reg.getReference();
}
return null;
}
@Override
protected ServiceRegistration<S> getServiceRegistration()
{
if ( getComponentMetadata().getDSVersion() == DSVersion.DS12Felix )
{
return m_componentContext != null ? m_componentContext.getServiceRegistration() : null;
}
return super.getServiceRegistration();
}
private void updateServiceRegistration()
{
ServiceRegistration<S> sr = getServiceRegistration();
if ( sr != null )
{
try
{
// Don't propagate if service properties did not change.
final Dictionary<String, Object> regProps = getServiceProperties();
if ( !servicePropertiesMatches( sr, regProps ) )
{
sr.setProperties( regProps );
}
else
{
getLogger().log( LogService.LOG_DEBUG, "Not updating service registration, no change in properties", null );
}
}
catch ( final IllegalStateException ise )
{
// service has been unregistered asynchronously, ignore
}
catch ( final IllegalArgumentException iae )
{
getLogger().log( LogService.LOG_ERROR,
"Unexpected configuration property problem when updating service registration", iae );
}
catch ( final Throwable t )
{
getLogger().log( LogService.LOG_ERROR, "Unexpected problem when updating service registration", t );
}
}
else
{
getLogger().log( LogService.LOG_DEBUG, "No service registration to update", null );
}
}
/**
* Called by the Configuration Admin Service to update the component with
* Configuration properties.
* <p/>
* This causes the component to be reactivated with the new configuration
* unless no configuration has ever been set on this component and the
* <code>configuration</code> parameter is <code>null</code>. In this case
* nothing is to be done. If a configuration has previously been set and
* now the configuration is deleted, the <code>configuration</code>
* parameter is <code>null</code> and the component has to be reactivated
* with the default configuration.
*
* @param configuration The configuration properties for the component from
* the Configuration Admin Service or <code>null</code> if there is
* no configuration or if the configuration has just been deleted.
* @param configurationDeleted TODO
* @param factoryPid TODO
*/
@Override
public void reconfigure( Map<String, Object> configuration, boolean configurationDeleted, TargetedPID factoryPid)
{
// store the properties
m_configurationProperties = configuration;
reconfigure(configurationDeleted);
}
void reconfigure(boolean configurationDeleted)
{
Deferred<Void> enableLatch = enableLatchWait();
try
{
// clear the current properties to force using the configuration data
m_properties = null;
// reactivate the component to ensure it is provided with the
// configuration data
if ( !getState().isEnabled() )
{
// nothing to do for inactive components, leave this method
getLogger().log( LogService.LOG_DEBUG, "Component can not be activated since it is in state {0}", null, getState() );
//enabling the component will set the target properties, do nothing now.
return;
}
// unsatisfied component and non-ignored configuration may change targets
// to satisfy references
obtainActivationWriteLock( );
try
{
if ( !getState().isSatisfied() && !getComponentMetadata().isConfigurationIgnored() )
{
getLogger().log( LogService.LOG_DEBUG, "Attempting to activate unsatisfied component", null );
updateTargets( getProperties() );
releaseActivationWriteeLock( );
activateInternal( );
return;
}
if ( !modify(configurationDeleted) )
{
// SCR 112.7.1 - deactivate if configuration is deleted or no modified method declared
getLogger().log( LogService.LOG_DEBUG, "Deactivating and Activating to reconfigure from configuration", null );
int reason = configurationDeleted ? ComponentConstants.DEACTIVATION_REASON_CONFIGURATION_DELETED
: ComponentConstants.DEACTIVATION_REASON_CONFIGURATION_MODIFIED;
// FELIX-2368: cycle component immediately, reconfigure() is
// called through ConfigurationListener API which itself is
// called asynchronously by the Configuration Admin Service
releaseActivationWriteeLock( );
//we have already determined that modify cannot be called. Therefore factory instances must be disposed.
boolean dispose = m_factoryInstance;
deactivateInternal( reason, dispose, dispose );
if ( !dispose )
{
obtainActivationWriteLock();
try
{
updateTargets(getProperties());
}
finally
{
releaseActivationWriteeLock();
}
activateInternal();
}
}
}
finally
{
//used if modify succeeds or if there's an exception.
releaseActivationWriteeLock( );
}
}
finally
{
enableLatch.resolve(null);
}
}
private boolean modify(boolean configurationDeleted)
{
//0 SCR 112.7.1 If configuration is deleted, and version is < 1.3 and no flag set, then deactivate unconditionally.
// For version 1.3 and later, or with a flag, more sensible behavior is allowed.
if ( configurationDeleted && !getComponentMetadata().isDeleteCallsModify()){
return false;
}
// 1. no live update if there is no declared method
if ( getComponentMetadata().getModified() == null )
{
getLogger().log( LogService.LOG_DEBUG, "No modified method, cannot update dynamically", null );
return false;
}
// invariant: we have a modified method name
// 2. get and check configured method
// invariant: modify method is configured and found
// 3. check whether we can dynamically apply the configuration if
// any target filters influence the bound services
final Map<String, Object> props = getProperties();
for ( DependencyManager<S, ?> dm: getDependencyManagers() )
{
if ( !dm.canUpdateDynamically( props ) )
{
getLogger().log( LogService.LOG_DEBUG,
"Cannot dynamically update the configuration due to dependency changes induced on dependency {0}",
null, dm.getName() );
return false;
}
}
// invariant: modify method existing and no static bound service changes
// 4. call method (nothing to do when failed, since it has already been logged)
// (call with non-null default result to continue even if the
// modify method call failed)
obtainStateLock( );
try
{
//cf 112.5.12 where invoking modified method before updating target services is specified.
final MethodResult result = invokeModifiedMethod();
updateTargets( props );
if ( result == null )
{
// log an error if the declared method cannot be found
getLogger().log( LogService.LOG_ERROR, "Declared modify method ''{0}'' cannot be found, configuring by reactivation",
null, getComponentMetadata().getModified() );
return false;
}
// 5. update the target filter on the services now, this may still
// result in unsatisfied dependencies, in which case we abort
// this dynamic update and have the component be deactivated
if ( !verifyDependencyManagers() )
{
getLogger().log( LogService.LOG_DEBUG,
"Updating the service references caused at least one reference to become unsatisfied, deactivating component",
null );
return false;
}
// 6. update service registration properties if we didn't just do it
if ( result.hasResult() )
{
setServiceProperties( result, null );
}
else
{
updateServiceRegistration();
}
// 7. everything set and done, the component has been updated
return true;
}
finally
{
releaseStateLock( );
}
}
protected MethodResult invokeModifiedMethod()
{
LifecycleMethod modifiedMethod = getComponentMethods().getModifiedMethod();
if ( getInstance() != null )
{
return modifiedMethod.invoke( getInstance(), m_componentContext, -1,
MethodResult.VOID );
}
return MethodResult.VOID;
}
/**
* Checks if the given service registration properties matches another set
* of properties.
*
* @param reg the service registration whose service properties will be
* compared to the props parameter
* @param props the properties to be compared with the registration
* service properties.
* @return <code>true</code> if the registration service properties equals
* the prop properties, false if not.
*/
private boolean servicePropertiesMatches( ServiceRegistration<S> reg, Dictionary<String, Object> props )
{
Dictionary<String, Object> regProps = new Hashtable<>();
String[] keys = reg.getReference().getPropertyKeys();
for ( int i = 0; keys != null && i < keys.length; i++ )
{
if ( !keys[i].equals( org.osgi.framework.Constants.OBJECTCLASS )
&& !keys[i].equals( org.osgi.framework.Constants.SERVICE_ID ) )
{
regProps.put( keys[i], reg.getReference().getProperty( keys[i] ) );
}
}
return regProps.equals( props );
}
@Override
public S getService( final Bundle bundle, final ServiceRegistration<S> serviceRegistration )
{
ServiceReference<S> ref = null;
try
{
ref = serviceRegistration.getReference();
}
catch ( final IllegalStateException ise)
{
// sanity test for service already unregistered
return null;
}
if ( m_container.getActivator().enterCreate( ref ) )
{
// circular dependency
return null;
}
try
{
obtainStateLock();
try
{
m_useCount.incrementAndGet();
}
finally
{
releaseStateLock( );
}
boolean decrement = true;
try
{
boolean success = getServiceInternal(serviceRegistration);
ComponentContextImpl<S> componentContext = m_componentContext;
if ( success && componentContext != null)
{
decrement = false;
return componentContext.getImplementationObject( true );
}
else
{
return null;
}
}
finally
{
if ( decrement )
{
ungetService( bundle, serviceRegistration, null );
}
}
}
finally
{
m_container.getActivator().leaveCreate( ref );
}
}
@Override
boolean getServiceInternal(ServiceRegistration<S> serviceRegistration)
{
boolean success = true;
if ( m_componentContext == null )
{
ComponentContextImpl<S> componentContext = new ComponentContextImpl<>(this, this.getBundle(), serviceRegistration);
if ( collectDependencies(componentContext))
{
getLogger().log( LogService.LOG_DEBUG,
"getService (single component manager) dependencies collected.",
null );
}
else
{
getLogger().log( LogService.LOG_INFO,
"Could not obtain all required dependencies, getService returning null",
null );
return false;
}
obtainStateLock( );
try
{
if ( m_componentContext == null )
{
State previousState = getState();
//state should be "Registered"
S result = getService(componentContext );
if ( result == null )
{
success = false;
}
else
{
setState(previousState, State.active);
}
}
}
finally
{
releaseStateLock( );
}
}
return success;
}
private S getService(ComponentContextImpl<S> componentContext)
{
//should be write locked
if (!getState().isEnabled())
{
return null;
}
if ( createComponent(componentContext) )
{
return getInstance();
}
// log that the delayed component cannot be created (we don't
// know why at this moment; this should already have been logged)
getLogger().log( LogService.LOG_DEBUG, "Failed creating the component instance; see log for reason", null );
// component could not really be created. This may be temporary
// so we stay in the registered state but ensure the component
// instance is deleted
try
{
deleteComponent( ComponentConstants.DEACTIVATION_REASON_UNSPECIFIED );
}
catch ( final Throwable t )
{
getLogger().log( LogService.LOG_DEBUG, "Cannot delete incomplete component instance. Ignoring.", t );
}
// no service can be returned (be prepared for more logging !!)
return null;
}
@Override
public void ungetService( final Bundle bundle, final ServiceRegistration<S> serviceRegistration, final S o )
{
obtainStateLock( );
try
{
// unget the service instance if no bundle is using it
// any longer unless delayed component instances have to
// be kept (FELIX-3039)
if ( m_useCount.decrementAndGet() == 0 && !isImmediate() && !keepInstances() )
{
final State previousState = getState();
deleteComponent( ComponentConstants.DEACTIVATION_REASON_UNSPECIFIED );
setState(previousState, State.satisfied);
}
}
finally
{
releaseStateLock( );
}
}
private boolean keepInstances()
{
return getComponentMetadata().isDelayedKeepInstances();
}
@Override
public void getComponentManagers(List<AbstractComponentManager<S>> cms)
{
cms.add(this);
}
}