blob: 38fe9eb4188e24fe3eb264fdc5ed7b57b0b685fa [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;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import org.apache.felix.scr.Reference;
import org.osgi.framework.Bundle;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.ComponentInstance;
import org.osgi.service.log.LogService;
/**
* The default ComponentManager. Objects of this class are responsible for managing
* implementation object's lifecycle.
*
*/
abstract class AbstractComponentManager implements ComponentManager, ComponentInstance
{
// the ID of this component
private long m_componentId;
// The state of this instance manager
private volatile State m_state;
// The metadata
private ComponentMetadata m_componentMetadata;
// The dependency managers that manage every dependency
private List m_dependencyManagers;
// A reference to the BundleComponentActivator
private BundleComponentActivator m_activator;
// The ServiceRegistration
private ServiceRegistration m_serviceRegistration;
/**
* There are 9 states in all. They are: Disabled, Enabled, Unsatisfied,
* Registered, Factory, Active, Destroyed, Activating and Deactivating.
* State Enabled is right State Unsatisfied. State Registered, Factory
* and Active are the "Satisfied" state in concept. State Activating and
* Deactivating are transition states. They will be changed to other state
* automatically when work is done.
* <p>
* The transition cases are listed below.
* <ul>
* <li>Disabled -(enable)-> Enabled</li>
* <li>Disabled -(dispose)-> Destoryed</li>
* <li>Enabled -(activate, SUCCESS)-> Satisfied(Registered, Factory or Active)</li>
* <li>Enabled -(activate, FAIL)-> Unsatisfied</li>
* <li>Enabled -(disable)-> Disabled</li>
* <li>Enabled -(dispose)-> Destroyed</li>
* <li>Unsatisfied -(activate, SUCCESS)-> Satisfied(Registered, Factory or Active)</li>
* <li>Unsatisfied -(activate, FAIL)-> Unsatisfied</li>
* <li>Unsatisfied -(disable)-> Disabled</li>
* <li>Unsatisfied -(dispose)-> Destroyed</li>
* <li>Registered -(getService, SUCCESS)-> Active</li>
* <li>Registered -(getService, FAIL)-> Unsatisfied</li>
* <li>Satisfied -(deactivate)-> Unsatisfied</li>
* </ul>
*/
protected static abstract class State
{
private final String m_name;
private final int m_state;
protected State( String name, int state )
{
m_name = name;
m_state = state;
}
public String toString()
{
return m_name;
}
int getState()
{
return m_state;
}
ServiceReference getServiceReference( AbstractComponentManager acm )
{
return null;
}
void enableInternal( AbstractComponentManager acm )
{
acm.log( LogService.LOG_DEBUG,
"Current state: " + m_name + ", Event: enable",
acm.getComponentMetadata(), null );
}
void disableInternal( AbstractComponentManager acm )
{
acm.log( LogService.LOG_DEBUG,
"Current state: " + m_name + ", Event: disable",
acm.getComponentMetadata(), null );
}
void activateInternal( AbstractComponentManager acm )
{
acm.log(LogService.LOG_DEBUG,
"Current state: " + m_name + ", Event: activate",
acm.getComponentMetadata(), null);
}
void deactivateInternal( AbstractComponentManager acm )
{
acm.log( LogService.LOG_DEBUG,
"Current state: " + m_name + ", Event: deactivate",
acm.getComponentMetadata(), null );
}
void disposeInternal( AbstractComponentManager acm )
{
acm.log( LogService.LOG_DEBUG,
"Current state: " + m_name + ", Event: dispose",
acm.getComponentMetadata(), null );
}
Object getService( DelayedComponentManager dcm )
{
dcm.log( LogService.LOG_DEBUG,
"Current state: " + m_name + ", Event: getService",
dcm.getComponentMetadata(), null );
return null;
}
}
protected static final class Destroyed extends State
{
private static final Destroyed m_inst = new Destroyed();
private Destroyed()
{
super( "Destroyed", STATE_DESTROYED );
}
static State getInstance()
{
return m_inst;
}
}
protected static final class Disabled extends State
{
private static final Disabled m_inst = new Disabled();
private Disabled()
{
super( "Disabled", STATE_DISABLED );
}
static State getInstance()
{
return m_inst;
}
void enableInternal( AbstractComponentManager acm )
{
acm.changeState( Enabled.getInstance() );
acm.log( LogService.LOG_DEBUG, "Component enabled", acm.getComponentMetadata(), null );
}
void disposeInternal( AbstractComponentManager acm )
{
acm.clear();
acm.changeState( Destroyed.getInstance() );
}
}
protected static final class Enabled extends State
{
private static final Enabled m_inst = new Enabled();
private Enabled()
{
super( "Enabled", STATE_ENABLED );
}
static State getInstance()
{
return m_inst;
}
void activateInternal( AbstractComponentManager acm )
{
ComponentMetadata componentMetadata = acm.getComponentMetadata();
try
{
acm.enableDependencyManagers();
Unsatisfied.getInstance().activateInternal(acm);
}
catch (Exception e)
{
acm.log( LogService.LOG_ERROR, "Failed enabling Component", componentMetadata, e );
acm.disposeDependencyManagers();
acm.loadDependencyManagers( acm.getComponentMetadata() );
}
}
void disableInternal( AbstractComponentManager acm )
{
acm.changeState( Disabled.getInstance() );
acm.log( LogService.LOG_DEBUG, "Component disabled", acm.getComponentMetadata(), null );
}
void disposeInternal( AbstractComponentManager acm )
{
acm.clear();
acm.changeState( Destroyed.getInstance() );
acm.log( LogService.LOG_DEBUG, "Component disposed", acm.getComponentMetadata(), null );
}
}
protected static final class Unsatisfied extends State
{
private static final Unsatisfied m_inst = new Unsatisfied();
private Unsatisfied()
{
super( "Unsatisfied", STATE_UNSATISFIED );
}
static State getInstance()
{
return m_inst;
}
void activateInternal( AbstractComponentManager acm )
{
acm.changeState( Activating.getInstance() );
ComponentMetadata componentMetadata = acm.getComponentMetadata();
acm.log( LogService.LOG_DEBUG, "Activating component", componentMetadata, null );
// Before creating the implementation object, we are going to
// test if all the mandatory dependencies are satisfied
if ( !acm.verifyDependencyManagers( acm.getProperties()) )
{
acm.log( LogService.LOG_INFO, "Not all dependencies satisified, cannot activate", componentMetadata, null );
acm.changeState( Unsatisfied.getInstance() );
return;
}
// 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 ( !acm.createComponent() )
{
// component creation failed, not active now
acm.log( LogService.LOG_ERROR, "Component instance could not be created, activation failed",
componentMetadata, null );
// set state to unsatisfied
acm.changeState( Unsatisfied.getInstance() );
return;
}
acm.changeState( acm.getSatisfiedState() );
acm.registerComponentService();
}
void disableInternal( AbstractComponentManager acm )
{
ComponentMetadata componentMetadata = acm.getComponentMetadata();
acm.log( LogService.LOG_DEBUG, "Disabling component", componentMetadata, null );
// dispose and recreate dependency managers
acm.disposeDependencyManagers();
acm.loadDependencyManagers( componentMetadata );
// we are now disabled, ready for re-enablement or complete destroyal
acm.changeState( Disabled.getInstance() );
acm.log( LogService.LOG_DEBUG, "Component disabled", componentMetadata, null );
}
void disposeInternal( AbstractComponentManager acm )
{
acm.disposeDependencyManagers();
acm.clear();
acm.changeState( Destroyed.getInstance() );
acm.log( LogService.LOG_DEBUG, "Component disposed", acm.getComponentMetadata(), null );
}
}
protected static final class Activating extends State
{
private static final Activating m_inst = new Activating();
private Activating() {
super( "Activating", STATE_ACTIVATING );
}
static State getInstance()
{
return m_inst;
}
}
protected static final class Deactivating extends State
{
private static final Deactivating m_inst = new Deactivating();
private Deactivating() {
super( "Deactivating", STATE_DEACTIVATING );
}
static State getInstance() {
return m_inst;
}
}
protected static abstract class Satisfied extends State
{
protected Satisfied( String name, int state )
{
super( name, state );
}
ServiceReference getServiceReference( AbstractComponentManager acm )
{
ServiceRegistration sr = acm.getServiceRegistration();
return sr == null ? null : sr.getReference();
}
void deactivateInternal( AbstractComponentManager acm )
{
acm.changeState(Deactivating.getInstance());
ComponentMetadata componentMetadata = acm.getComponentMetadata();
acm.log( LogService.LOG_DEBUG, "Deactivating component", componentMetadata, null );
acm.unregisterComponentService();
acm.deleteComponent();
acm.changeState( Unsatisfied.getInstance() );
acm.log( LogService.LOG_DEBUG, "Component deactivated", componentMetadata, null );
}
}
protected static final class Registered extends Satisfied
{
private static final Registered m_inst = new Registered();
private Registered() {
super( "Registered", STATE_REGISTERED );
}
static State getInstance()
{
return m_inst;
}
Object getService(DelayedComponentManager dcm)
{
if ( dcm.createRealComponent() )
{
dcm.changeState( Active.getInstance() );
return dcm.getInstance();
}
else
{
deactivateInternal( dcm );
return null;
}
}
}
protected static final class Factory extends Satisfied
{
private static final Factory m_inst = new Factory();
private Factory()
{
super( "Factory", STATE_FACTORY );
}
static State getInstance()
{
return m_inst;
}
}
protected static final class Active extends Satisfied
{
private static final Active m_inst = new Active();
private Active()
{
super( "Active", STATE_ACTIVE );
}
static State getInstance()
{
return m_inst;
}
Object getService( DelayedComponentManager dcm )
{
return dcm.getInstance();
}
}
/**
* The constructor receives both the activator and the metadata
*
* @param activator
* @param metadata
* @param componentId
*/
protected AbstractComponentManager( BundleComponentActivator activator,
ComponentMetadata metadata, long componentId )
{
m_activator = activator;
m_componentMetadata = metadata;
m_componentId = componentId;
m_state = Disabled.getInstance();
loadDependencyManagers( metadata );
log( LogService.LOG_DEBUG, "Component created", metadata, null );
}
//---------- Asynchronous frontend to state change methods ----------------
/**
* 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 schedules the enablement for asynchronous execution.
*/
public final void enable()
{
enableInternal();
getActivator().schedule( new ComponentActivatorTask( "Enable", this )
{
public void doRun()
{
activateInternal();
}
});
}
/**
* Disables this component and - if active - first deactivates it. The
* component may be reenabled by calling the {@link #enable()} method.
* <p>
* This method schedules the disablement for asynchronous execution.
*/
public final void disable()
{
getActivator().schedule( new ComponentActivatorTask( "Disable", this )
{
public void doRun()
{
deactivateInternal();
disableInternal();
}
});
}
/**
* 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()
{
disposeInternal();
}
//---------- Component interface ------------------------------------------
public long getId()
{
return m_componentId;
}
public String getName() {
return m_componentMetadata.getName();
}
public Bundle getBundle()
{
return getActivator().getBundleContext().getBundle();
}
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 boolean isServiceFactory()
{
return m_componentMetadata.getServiceMetadata() != null
&& m_componentMetadata.getServiceMetadata().isServiceFactory();
}
public String[] getServices()
{
if ( m_componentMetadata.getServiceMetadata() != null )
{
return m_componentMetadata.getServiceMetadata().getProvides();
}
return null;
}
//-------------- atomic transition methods -------------------------------
/**
* 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.
*/
synchronized final void enableInternal()
{
m_state.enableInternal( this );
}
synchronized final void disableInternal()
{
m_state.disableInternal( this );
}
synchronized final void activateInternal()
{
m_state.activateInternal( this );
}
synchronized final void deactivateInternal()
{
m_state.deactivateInternal( this );
}
synchronized final void disposeInternal()
{
m_state.deactivateInternal( this );
// For the sake of the performance(no need to loadDependencyManagers again),
// the disable transition is integrated into the destroy transition.
// That is to say, state "Enabled" goes directly into state "Desctroyed"
// in the event "dipose".
m_state.disposeInternal( this );
}
synchronized final ServiceReference getServiceReference()
{
return m_state.getServiceReference( this );
}
//---------- Component handling methods ----------------------------------
/**
* Method is called by {@link #activate()} 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();
/**
* Returns the service object to be registered if the service element is
* specified.
* <p>
* Extensions of this class may overwrite this method to return a
* ServiceFactory to register in the case of a delayed or a service
* factory component.
*
* @return
*/
protected abstract Object getService();
final State getSatisfiedState()
{
if ( m_componentMetadata.isFactory() )
{
return Factory.getInstance();
}
else if ( m_componentMetadata.isImmediate() )
{
return Active.getInstance();
}
else
{
return Registered.getInstance();
}
}
protected ServiceRegistration registerService()
{
if ( getComponentMetadata().getServiceMetadata() != null )
{
log( LogService.LOG_DEBUG, "registering services", m_componentMetadata, null );
// get a copy of the component properties as service properties
Dictionary serviceProperties = copyTo( null, getProperties() );
return getActivator().getBundleContext().registerService(
getComponentMetadata().getServiceMetadata().getProvides(),
getService(), serviceProperties );
}
return null;
}
// 5. Register provided services
protected void registerComponentService()
{
m_serviceRegistration = registerService();
}
protected void unregisterComponentService()
{
if ( m_serviceRegistration != null )
{
log( LogService.LOG_DEBUG, "unregistering the services", m_componentMetadata, null );
m_serviceRegistration.unregister();
m_serviceRegistration = null;
}
}
//**********************************************************************************************************
BundleComponentActivator getActivator()
{
return m_activator;
}
final ServiceRegistration getServiceRegistration()
{
return m_serviceRegistration;
}
void clear()
{
m_activator = null;
m_dependencyManagers.clear();
}
void log( int level, String message, ComponentMetadata metadata, Throwable ex )
{
BundleComponentActivator activator = getActivator();
if ( activator != null )
{
activator.log( level, message, metadata, ex );
}
}
/**
* Activates this component if satisfied. If any of the dependencies is
* not met, the component is not activated and remains unsatisifed.
* <p>
* This method schedules the activation for asynchronous execution.
*/
public final void activate()
{
getActivator().schedule( new ComponentActivatorTask( "Activate", this ) {
public void doRun()
{
activateInternal();
}
});
}
/**
* Reconfigures this component by deactivating and activating it. During
* activation the new configuration data is retrieved from the Configuration
* Admin Service.
*/
public final void reconfigure()
{
log( LogService.LOG_DEBUG, "Deactivating and Activating to reconfigure",
m_componentMetadata, null );
reactivate();
}
/**
* Cycles this component by deactivating it and - if still satisfied -
* activating it again asynchronously.
* <p>
* This method schedules the deactivation and reactivation for asynchronous
* execution.
*/
public final void reactivate()
{
getActivator().schedule(new ComponentActivatorTask( "Reactivate", this ) {
public void doRun()
{
deactivateInternal();
activateInternal();
}
});
}
public String toString() {
return "Component: " + getName() + " (" + getId() + ")";
}
private void loadDependencyManagers( ComponentMetadata metadata )
{
List depMgrList = new ArrayList();
// If this component has got dependencies, create dependency managers for each one of them.
if ( metadata.getDependencies().size() != 0 )
{
Iterator dependencyit = metadata.getDependencies().iterator();
while ( dependencyit.hasNext() )
{
ReferenceMetadata currentdependency = (ReferenceMetadata) dependencyit.next();
DependencyManager depmanager = new DependencyManager( this, currentdependency );
depMgrList.add( depmanager );
}
}
m_dependencyManagers = depMgrList;
}
private void enableDependencyManagers() throws InvalidSyntaxException
{
Iterator it = getDependencyManagers();
while ( it.hasNext() )
{
DependencyManager dm = (DependencyManager) it.next();
dm.enable();
}
}
private boolean verifyDependencyManagers( Dictionary properties )
{
// indicates whether all dependencies are satisfied
boolean satisfied = true;
Iterator it = getDependencyManagers();
while ( it.hasNext() )
{
DependencyManager dm = (DependencyManager) it.next();
// ensure the target filter is correctly set
dm.setTargetFilter( properties );
// check whether the service is satisfied
if ( !dm.isSatisfied() )
{
// at least one dependency is not satisfied
log( LogService.LOG_INFO, "Dependency not satisfied: " + dm.getName(), m_componentMetadata, null );
satisfied = false;
}
}
return satisfied;
}
Iterator getDependencyManagers()
{
return m_dependencyManagers.iterator();
}
DependencyManager getDependencyManager(String name)
{
Iterator it = getDependencyManagers();
while ( it.hasNext() )
{
DependencyManager dm = (DependencyManager) it.next();
if ( name.equals(dm.getName()) )
{
return dm;
}
}
// not found
return null;
}
private void disposeDependencyManagers()
{
Iterator it = getDependencyManagers();
while ( it.hasNext() )
{
DependencyManager dm = (DependencyManager) it.next();
dm.dispose();
}
}
/**
* Get the object that is implementing this descriptor
*
* @return the object that implements the services
*/
public abstract Object getInstance();
public abstract Dictionary getProperties();
/**
* 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 Dictionary copyTo( Dictionary target, Dictionary source )
{
if ( target == null )
{
target = new Hashtable();
}
if ( source != null && !source.isEmpty() )
{
for ( Enumeration ce = source.keys(); ce.hasMoreElements(); )
{
Object key = ce.nextElement();
target.put( key, source.get( key ) );
}
}
return target;
}
/**
*
*/
public ComponentMetadata getComponentMetadata()
{
return m_componentMetadata;
}
public int getState()
{
return m_state.getState();
}
protected State state()
{
return m_state;
}
/**
* sets the state of the manager
**/
void changeState( State newState )
{
log( LogService.LOG_DEBUG,
"State transition : " + m_state + " -> " + newState,
m_componentMetadata, null );
m_state = newState;
}
/**
* Finds the named public or protected method in the given class or any
* super class. If such a method is found, its accessibility is enfored by
* calling the <code>Method.setAccessible</code> method if required and
* the method is returned. Enforcing accessibility is required to support
* invocation of protected methods.
*
* @param clazz The <code>Class</code> which provides the method.
* @param name The name of the method.
* @param parameterTypes The parameters to the method. Passing
* <code>null</code> is equivalent to using an empty array.
* @param only Whether to only look at the declared methods of the given
* class or also inspect the super classes.
*
* @return The named method with enforced accessibility
*
* @throws NoSuchMethodException If no public or protected method with
* the given name can be found in the class or any of its super classes.
* @throws InvocationTargetException If an unexpected Throwable is caught
* trying to access the desired method.
*/
static Method getMethod( Class clazz, String name, Class[] parameterTypes, boolean only )
throws NoSuchMethodException, InvocationTargetException
{
for ( ; clazz != null; clazz = clazz.getSuperclass() )
{
try
{
// find the declared method in this class
Method method = clazz.getDeclaredMethod( name, parameterTypes );
// accept public and protected methods only and ensure accessibility
if ( Modifier.isPublic( method.getModifiers() ) || Modifier.isProtected( method.getModifiers()) )
{
method.setAccessible( true );
return method;
}
// if only the clazz is to be scanned terminate here
if ( only )
{
break;
}
}
catch ( NoSuchMethodException nsme )
{
// ignore for now
}
catch ( Throwable throwable )
{
// unexpected problem accessing the method, don't let everything
// blow up in this situation, just throw a declared exception
throw new InvocationTargetException( throwable,
"Unexpected problem trying to get method " + name );
}
}
// walked up the complete super class hierarchy and still not found
// anything, sigh ...
throw new NoSuchMethodException( name );
}
}