blob: 9e6a869ca4138cd32ae2cfd78f2425870476cf8a [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;
import java.io.IOException;
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.HashSet;
import java.util.Hashtable;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.Configuration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.ComponentConstants;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.ComponentInstance;
/**
* The default ComponentManager. Objects of this class are responsible for managing
* implementation object's lifecycle.
*
*/
abstract class AbstractComponentManager implements ComponentManager, ComponentInstance
{
// manager has been newly created or disabled
static final int STATE_DISABLED = 1;
// manager has just been enabled and is going to be activated
static final int STATE_ENABLED = 2;
// manager has been enabled but not satisfied
static final int STATE_UNSATISFIED = 4;
// manager is currently activating
static final int STATE_ACTIVATING = 8;
// manager is now active
static final int STATE_ACTIVE = 16;
// manager for a delayed component has been registered (not active yet)
static final int STATE_REGISTERED = 32;
// manager for a component factory has been registered
static final int STATE_FACTORY = 64;
// manager is current deactivating
static final int STATE_DEACTIVATING = 128;
// manager has been destroyed and may not be used anymore
static final int STATE_DESTROYED = 256;
// The state of this instance manager
private int 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;
/**
* The constructor receives both the activator and the metadata
*
* @param activator
* @param metadata
*/
protected AbstractComponentManager(BundleComponentActivator activator, ComponentMetadata metadata)
{
m_activator = activator;
m_componentMetadata = metadata;
m_state = STATE_DISABLED;
m_dependencyManagers = new ArrayList();
Activator.trace("Component created", m_componentMetadata);
}
//---------- 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() {
getActivator().schedule( new Runnable()
{
public void run()
{
enableInternal();
}
} );
}
/**
* 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 Runnable()
{
public void run()
{
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()
{
Activator.trace( "Deactivating and Activating to reconfigure", m_componentMetadata );
reactivate();
}
/**
* Cycles this component by deactivating it and - if still satisfied -
* activating it again.
* <p>
* This method schedules the reactivation for asynchronous execution.
*/
public final void reactivate() {
getActivator().schedule( new Runnable()
{
public void run()
{
deactivateInternal();
Activator.trace( "Dependency Manager: RECREATING", m_componentMetadata );
activateInternal();
}
} );
}
/**
* Deactivates the component.
* <p>
* This method schedules the deactivation for asynchronous execution.
*/
public final void deactivate() {
getActivator().schedule( new Runnable()
{
public void run()
{
deactivateInternal();
}
} );
}
/**
* 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 Runnable()
{
public void run()
{
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 final void dispose() {
disposeInternal();
}
//---------- internal immediate state change methods ----------------------
// these methods must only be called from a separate thread by calling
// the respective asynchronous (public) method
/**
* Enable this component
*
* @return true if enabling was successful
*/
private void enableInternal() {
if (getState() == STATE_DESTROYED)
{
Activator.error( "Destroyed Component cannot be enabled", m_componentMetadata );
return;
}
else if (getState() != STATE_DISABLED)
{
Activator.trace( "Component is already enabled", m_componentMetadata );
return;
}
Activator.trace("Enabling component", m_componentMetadata);
try
{
// If this component has got dependencies, create dependency managers for each one of them.
if (m_componentMetadata.getDependencies().size() != 0)
{
Iterator dependencyit = m_componentMetadata.getDependencies().iterator();
while(dependencyit.hasNext())
{
ReferenceMetadata currentdependency = (ReferenceMetadata)dependencyit.next();
DependencyManager depmanager = new DependencyManager(this, currentdependency);
m_dependencyManagers.add(depmanager);
}
}
// enter enabled state before trying to activate
setState( STATE_ENABLED );
Activator.trace("Component enabled", m_componentMetadata);
// immediately activate the compopnent, no need to schedule again
activateInternal();
}
catch(Exception ex)
{
Activator.exception( "Failed enabling Component", m_componentMetadata, ex );
// ensure we get back to DISABLED state
// immediately disable, no need to schedule again
disableInternal();
}
}
/**
* Activate this Instance manager.
*
* 112.5.6 Activating a component configuration consists of the following steps
* 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
* [5. Register provided services]
*/
private void activateInternal()
{
// CONCURRENCY NOTE: This method is called either by the enable()
// method or by the dependency managers or the reconfigure() method
if ( (getState() & (STATE_ENABLED|STATE_UNSATISFIED)) == 0)
{
// This state can only be entered from the ENABLED (in the enable()
// method) or UNSATISFIED (missing references) states
return;
}
// go to the activating state
setState(STATE_ACTIVATING);
Activator.trace("Activating component", m_componentMetadata);
// Before creating the implementation object, we are going to
// test if all the mandatory dependencies are satisfied
Iterator it = m_dependencyManagers.iterator();
while (it.hasNext())
{
DependencyManager dm = (DependencyManager)it.next();
if (!dm.isValid())
{
// at least one dependency is not satisfied
Activator.trace( "Dependency not satisfied: " + dm.getName(), m_componentMetadata );
setState(STATE_UNSATISFIED);
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
createComponent();
// Validation occurs before the services are provided, otherwhise the
// service provider's service may be called by a service requester
// while it is still ACTIVATING
setState(getSatisfiedState());
// 5. Register provided services
m_serviceRegistration = registerComponentService();
Activator.trace("Component activated", m_componentMetadata);
}
/**
* This method deactivates the manager, performing the following steps
*
* [0. Remove published services from the registry]
* 1. Call the deactivate() method, if present
* 2. Unbind any bound services
* 3. Release references to the component instance and component context
**/
private void deactivateInternal()
{
// CONCURRENCY NOTE: This method may be called either from application
// code or by the dependency managers or reconfiguration
if ((getState() & (STATE_ACTIVATING|STATE_ACTIVE|STATE_REGISTERED|STATE_FACTORY)) == 0)
{
// This state can only be entered from the ACTIVATING (if activation
// fails), ACTIVE, REGISTERED or FACTORY states
return;
}
// start deactivation by resetting the state
setState( STATE_DEACTIVATING );
Activator.trace("Deactivating component", m_componentMetadata);
// 0.- Remove published services from the registry
unregisterComponentService();
// 1.- Call the deactivate method, if present
// 2. Unbind any bound services
// 3. Release references to the component instance and component context
deleteComponent();
//Activator.trace("InstanceManager from bundle ["+ m_activator.getBundleContext().getBundle().getBundleId() + "] was invalidated.");
// reset to state UNSATISFIED
setState( STATE_UNSATISFIED );
Activator.trace("Component deactivated", m_componentMetadata);
}
private void disableInternal()
{
// CONCURRENCY NOTE: This method is only called from the BundleComponentActivator or by application logic
// but not by the dependency managers
// deactivate first, this does nothing if not active/registered/factory
deactivateInternal();
Activator.trace("Disabling component", m_componentMetadata);
// close all service listeners now, they are recreated on enable
// Stop the dependency managers to listen to events...
Iterator it = m_dependencyManagers.iterator();
while (it.hasNext())
{
DependencyManager dm = (DependencyManager)it.next();
dm.close();
}
m_dependencyManagers.clear();
// we are now disabled, ready for re-enablement or complete destroyal
setState( STATE_DISABLED );
Activator.trace("Component disabled", m_componentMetadata);
}
/**
*
*/
private void disposeInternal()
{
// CONCURRENCY NOTE: This method is only called from the BundleComponentActivator or by application logic
// but not by the dependency managers
// disable first to clean up correctly
disableInternal();
// this component must not be used any more
setState( STATE_DESTROYED );
// release references (except component metadata for logging purposes)
m_activator = null;
m_dependencyManagers = null;
Activator.trace("Component disposed", m_componentMetadata);
}
//---------- Component handling methods ----------------------------------
/**
* Method is called by {@link #activate()} in STATE_ACTIVATING or by
* {@link DelayedComponentManager#getService(Bundle, ServiceRegistration)}
* in STATE_REGISTERED.
*/
protected abstract void createComponent();
/**
* Method is called by {@link #deactivate()} in STATE_DEACTIVATING
*
*/
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.
*/
protected abstract Object getService();
/**
* Returns the state value to set, when the component is satisfied. The
* return value depends on the kind of the component:
* <dl>
* <dt>Immediate</dt><dd><code>STATE_ACTIVE</code></dd>
* <dt>Delayed</dt><dd><code>STATE_REGISTERED</code></dd>
* <dt>Component Factory</dt><dd><code>STATE_FACTORY</code></dd>
* </dl>
*
* @return
*/
private int getSatisfiedState() {
if (m_componentMetadata.isFactory())
{
return STATE_FACTORY;
}
else if (m_componentMetadata.isImmediate())
{
return STATE_ACTIVE;
}
else
{
return STATE_REGISTERED;
}
}
// 5. Register provided services
protected ServiceRegistration registerComponentService()
{
if ( getComponentMetadata().getServiceMetadata() != null )
{
Activator.trace( "registering services", getComponentMetadata() );
// 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;
}
protected void unregisterComponentService()
{
if ( m_serviceRegistration != null )
{
m_serviceRegistration.unregister();
m_serviceRegistration = null;
Activator.trace( "unregistering the services", getComponentMetadata() );
}
}
//**********************************************************************************************************
BundleComponentActivator getActivator() {
return m_activator;
}
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() ) )
{
// only return the dm if it has service references
return ( dm.size() > 0 ) ? dm : null;
}
}
// not found
return null;
}
/**
* Get the object that is implementing this descriptor
*
* @return the object that implements the services
*/
public abstract Object getInstance();
protected 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;
}
ServiceReference getServiceReference() {
return ( m_serviceRegistration != null ) ? m_serviceRegistration.getReference() : null;
}
/**
*
*/
public ComponentMetadata getComponentMetadata() {
return m_componentMetadata;
}
int getState() {
return m_state;
}
/**
* sets the state of the manager
**/
protected synchronized void setState(int newState) {
Activator.trace( "State transition : " + stateToString( m_state ) + " -> " + stateToString( newState ),
m_componentMetadata );
m_state = newState;
}
public String stateToString(int state) {
switch (state) {
case STATE_DESTROYED:
return "Destroyed";
case STATE_DISABLED:
return "Disabled";
case STATE_ENABLED:
return "Enabled";
case STATE_UNSATISFIED:
return "Unsatisfied";
case STATE_ACTIVATING:
return "Activating";
case STATE_ACTIVE:
return "Active";
case STATE_REGISTERED:
return "Registered";
case STATE_FACTORY:
return "Factory";
case STATE_DEACTIVATING:
return "Deactivating";
default:
return String.valueOf(state);
}
}
/**
* 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.
*
* @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.
*/
static Method getMethod(Class clazz, String name, Class[] parameterTypes)
throws NoSuchMethodException
{
// try the default mechanism first, which only yields public methods
try
{
return clazz.getMethod(name, parameterTypes);
}
catch (NoSuchMethodException nsme)
{
// it is ok to not find a public method, try to find a protected now
}
// now use method declarations, requiring walking up the class
// hierarchy manually. this algorithm also returns protected methods
// which is, what we need here
for ( ; clazz != null; clazz = clazz.getSuperclass())
{
try
{
Method method = clazz.getDeclaredMethod(name, parameterTypes);
// only accept a protected method, a public method should
// have been found above and neither private nor package
// protected methods are acceptable here
if (Modifier.isProtected(method.getModifiers())) {
method.setAccessible(true);
return method;
}
}
catch (NoSuchMethodException nsme)
{
// ignore for now
}
}
// walked up the complete super class hierarchy and still not found
// anything, sigh ...
throw new NoSuchMethodException(name);
}
}