blob: d5af234680b867e7f00822e26e3ae7cc58404cb3 [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.lang.reflect.*;
import java.util.*;
import org.osgi.framework.*;
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.
*
*/
class ComponentManagerImpl implements ComponentManager, ComponentInstance
{
// States of the instance manager
static final int INSTANCE_CREATING = 0;
static final int INSTANCE_CREATED = 1;
static final int INSTANCE_VALIDATING = 2;
static final int INSTANCE_VALID = 3;
static final int INSTANCE_INVALIDATING = 4;
static final int INSTANCE_INVALID = 5;
static final int INSTANCE_DESTROYING = 6;
static final int INSTANCE_DESTROYED = 7;
static final String m_states[]={"CREATING","CREATED",
"VALIDATING","VALID",
"INVALIDATING","INVALID",
"DESTROYING","DESTROYED"
};
// The state of this instance manager
private int m_state = INSTANCE_CREATING;
// The metadata
private ComponentMetadata m_componentMetadata;
// The object that implements the service and that is bound to other services
private Object m_implementationObject = null;
// The dependency managers that manage every dependency
private List m_dependencyManagers = new ArrayList();
// The ServiceRegistration
private ServiceRegistration m_serviceRegistration = null;
// A reference to the BundleComponentActivator
private BundleComponentActivator m_activator = null;
// The context that will be passed to the implementationObject
private ComponentContext m_componentContext = null;
// In case of a delayed component, this holds a reference to the factory
private ServiceFactory m_delayedComponentServiceFactory;
/**
* The constructor receives both the activator and the metadata
*
* @param activator
* @param metadata
*/
ComponentManagerImpl(BundleComponentActivator activator, ComponentMetadata metadata)
{
// Store the activator reference
m_activator = activator;
// Store the metadata reference
m_componentMetadata = metadata;
}
/**
* Enable this component
*
* @return true if enabling was successful
*/
public boolean enable() {
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(currentdependency);
m_dependencyManagers.add(depmanager);
// Register the dependency managers as listeners to service events so that they begin
// to manage the dependency autonomously
m_activator.getBundleContext().addServiceListener(depmanager,depmanager.m_dependencyMetadata.getTarget());
}
}
// TODO: create the context
//m_sbcontext = new ServiceBinderContextImpl(this);
// Add this instance manager to the Generic activator list
//m_activator.addInstanceManager(this);
setState(INSTANCE_CREATED);
activate();
return true;
}
catch(Exception ex)
{
// TODO: Log this error
return false;
}
}
/**
* 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]
*/
synchronized private void activate()
{
// CONCURRENCY NOTE: This method is called either by the enable() method or by the dependency
// managers.
if (m_state != INSTANCE_INVALID && m_state !=INSTANCE_CREATED) {
// This state can only be entered from the CREATED or INVALID states
return;
}
setState(INSTANCE_VALIDATING);
// 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())
{
// It is not possible to call the isValid method yet in the DependencyManagers
// since they have not been initialized yet, but they can't be initialized yet
// because the implementationObject has not been created yet.
// This test is necessary, because we don't want to instantiate
// the implementationObject if the dependency managers aren't valid.
DependencyManager dm = (DependencyManager)it.next();
if (dm.getRequiredServiceRefs() == null && dm.m_dependencyMetadata.isOptional() == false)
{
setState(INSTANCE_INVALID);
return;
}
}
// 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
if( m_componentMetadata.isImmediate() == true )
{
//Activator.trace("Loading implementation class and creating instance for component '"+m_componentMetadata.getName()+"'");
try
{
// 112.4.4 The class is retrieved with the loadClass method of the component's bundle
Class c = m_activator.getBundleContext().getBundle().loadClass(m_componentMetadata.getImplementationClassName());
// 112.4.4 The class must be public and have a public constructor without arguments so component instances
// may be created by the SCR with the newInstance method on Class
m_componentContext = new ComponentContextImpl(null);
m_implementationObject = c.newInstance();
}
catch (Exception ex)
{
// TODO: manage this exception when implementation object cannot be created
Activator.exception("Error during instantiation", m_componentMetadata, ex);
deactivate();
//invalidate();
return;
}
}
else if ( m_componentMetadata.getServiceMetadata() != null
&& m_componentMetadata.getServiceMetadata().isServiceFactory() )
{
// delayed component is a ServiceFactory service
m_delayedComponentServiceFactory = new DelayedServiceFactoryServiceFactory();
}
else
{
// delayed component is a standard service
m_delayedComponentServiceFactory = new DelayedComponentServiceFactory();
}
// 3. Bind the target services
it = m_dependencyManagers.iterator();
//Activator.trace("Binding target services for component '"+m_componentMetadata.getName()+"'");
while (it.hasNext())
{
DependencyManager dm = (DependencyManager)it.next();
// if any of the dependency managers is unable to bind (it is invalid), the component is deactivated
if (dm.bind() == false)
{
deactivate();
return;
}
}
//Activator.trace("Calling activate for component '"+m_componentMetadata.getName()+"'");
// 4. Call the activate method, if present
// We need to check if we are still validating because it is possible that when we
// registered the service above our thread causes an instance to become valid which
// then registered a service that then generated an event that we needed that
// caused validate() to be called again, thus if we are not still VALIDATING, it
// means we are already VALID.
if ( m_componentMetadata.isImmediate() == true && m_state == INSTANCE_VALIDATING)
{
// Search for the activate method
try {
Method activateMethod = getMethod(m_implementationObject.getClass(), "activate", new Class[]{ComponentContext.class});
activateMethod.invoke(m_implementationObject, new Object[]{m_componentContext});
}
catch(NoSuchMethodException ex) {
// We can safely ignore this one
Activator.trace("activate() method not implemented", m_componentMetadata);
}
catch(IllegalAccessException ex) {
// TODO: Log this exception?
Activator.trace("activate() method cannot be called", m_componentMetadata);
}
catch(InvocationTargetException ex) {
// TODO: 112.5.8 If the activate method throws an exception, SCR must log an error message
// containing the exception with the Log Service
Activator.exception("The activate method has thrown an exception", m_componentMetadata, ex.getTargetException());
}
}
// Validation occurs before the services are provided, otherwhise the service provider's service may be called
// by a service requester while it is still VALIDATING
setState(INSTANCE_VALID);
// 5. Register provided services
if(m_componentMetadata.getServiceMetadata() != null)
{
Activator.trace("registering services", m_componentMetadata);
if( m_componentMetadata.isImmediate() == true ) {
// In the case the component is immediate, the implementation object is registered
m_serviceRegistration = m_activator.getBundleContext().registerService(m_componentMetadata.getServiceMetadata().getProvides(), m_implementationObject, m_componentMetadata.getProperties());
}else {
// In the case the component is delayed, a factory is registered
m_serviceRegistration = m_activator.getBundleContext().registerService(m_componentMetadata.getServiceMetadata().getProvides(), m_delayedComponentServiceFactory, m_componentMetadata.getProperties());
}
}
}
/**
* 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
**/
synchronized private void deactivate()
{
// CONCURRENCY NOTE: This method may be called either from application code or by the dependency managers
if (m_state != INSTANCE_VALID && m_state != INSTANCE_VALIDATING && m_state != INSTANCE_DESTROYING) {
return;
}
// In case the instance is valid when this is called, the manager is set to an invalidating state
if (m_state != INSTANCE_DESTROYING)
{
setState(INSTANCE_INVALIDATING);
}
// 0.- Remove published services from the registry
if(m_serviceRegistration != null)
{
m_serviceRegistration.unregister();
m_serviceRegistration = null;
Activator.trace("unregistering the services", m_componentMetadata);
}
// 1.- Call the deactivate method, if present
// Search the deactivate method
try {
// It is necessary to check that the implementation Object is not null. This may happen if the component
// is delayed and its service was never requested.
if(m_implementationObject != null)
{
Method activateMethod = getMethod(m_implementationObject.getClass(), "deactivate", new Class[]{ComponentContext.class});
activateMethod.invoke(m_implementationObject, new Object[]{m_componentContext});
}
}
catch(NoSuchMethodException ex) {
// We can safely ignore this one
Activator.trace("deactivate() method is not implemented", m_componentMetadata);
}
catch(IllegalAccessException ex) {
// Ignored, but should it be logged?
Activator.trace("deactivate() method cannot be called", m_componentMetadata);
}
catch(InvocationTargetException ex) {
// TODO: 112.5.12 If the deactivate method throws an exception, SCR must log an error message
// containing the exception with the Log Service
Activator.exception("The deactivate method has thrown and exception", m_componentMetadata, ex);
}
// 2. Unbind any bound services
Iterator it = m_dependencyManagers.iterator();
while (it.hasNext())
{
DependencyManager dm = (DependencyManager)it.next();
dm.unbind();
}
// 3. Release references to the component instance and component context
m_implementationObject = null;
m_componentContext = null;
m_delayedComponentServiceFactory = null;
//Activator.trace("InstanceManager from bundle ["+ m_activator.getBundleContext().getBundle().getBundleId() + "] was invalidated.");
if (m_state != INSTANCE_DESTROYING)
{
setState(INSTANCE_INVALID);
}
}
/**
*
*/
public synchronized void dispose()
{
// CONCURRENCY NOTE: This method is only called from the BundleComponentActivator or by application logic
// but not by the dependency managers
// Theoretically this should never be in any state other than VALID or INVALID,
// because validate is called right after creation.
if (m_state != INSTANCE_VALID && m_state != INSTANCE_INVALID)
{
return;
}
boolean deactivationRequired = (m_state == INSTANCE_VALID);
setState(INSTANCE_DESTROYING);
// Stop the dependency managers to listen to events...
Iterator it = m_dependencyManagers.iterator();
while (it.hasNext())
{
DependencyManager dm = (DependencyManager)it.next();
m_activator.getBundleContext().removeServiceListener(dm);
}
// in case the component is disposed when it was VALID, it is necessary to deactivate it first.
if (deactivationRequired)
{
deactivate();
}
m_dependencyManagers.clear();
setState(INSTANCE_DESTROYED);
m_activator = null;
}
//**********************************************************************************************************
/**
* Get the object that is implementing this descriptor
*
* @return the object that implements the services
*/
public Object getInstance() {
return m_implementationObject;
}
/**
*
*/
public ComponentMetadata getComponentMetadata() {
return m_componentMetadata;
}
/**
* sets the state of the manager
**/
private synchronized void setState(int newState) {
Activator.trace("State transition : "+m_states[m_state]+" -> "+m_states[newState], m_componentMetadata);
m_state = newState;
if(m_state == INSTANCE_CREATED || m_state == INSTANCE_VALID || m_state == INSTANCE_INVALID || m_state == INSTANCE_DESTROYED)
{
//m_activator.fireInstanceChangeEvent(new InstanceChangeEvent(this,m_instanceMetadata,m_state));
}
}
/**
* The DependencyManager task is to listen to service events and to call the * bind/unbind methods on a given object. It is also responsible for requesting * the unregistration of a service in case a dependency is broken.
*/
class DependencyManager implements ServiceListener
{
// Reference to the metadata
private ReferenceMetadata m_dependencyMetadata;
// The bound services <ServiceReference>
private Set m_boundServicesRefs = new HashSet();
// A flag that denotes if the dependency is satisfied at any given moment
private boolean m_isValid;
// A flag that defines if the bind method receives a ServiceReference
private boolean m_bindUsesServiceReference = false;
/**
* Constructor that receives several parameters.
*
* @param dependency An object that contains data about the dependency
**/
private DependencyManager(ReferenceMetadata dependency) throws ClassNotFoundException, NoSuchMethodException
{
m_dependencyMetadata = dependency;
m_isValid = false;
//m_bindMethod = getTargetMethod(m_dependencyMetadata.getBind(), m_componentMetadata.getImplementationClassName(),m_dependencyMetadata.getInterface());
//m_unbindMethod = getTargetMethod(m_dependencyMetadata.getUnbind(), m_componentMetadata.getImplementationClassName(),m_dependencyMetadata.getInterface());
}
/**
* initializes a dependency. This method binds all of the service occurrences to the instance object
*
* @return true if the operation was successful, false otherwise
**/
private boolean bind()
{
/*
if(getInstance() == null)
{
return false;
}*/
// Get service references
ServiceReference refs[] = getRequiredServiceRefs();
// If no references were received, we have to check if the dependency
// is optional, if it is not then the dependency is invalid
if (refs == null && m_dependencyMetadata.isOptional() == false)
{
m_isValid = false;
return m_isValid;
}
m_isValid = true;
// refs can be null if the dependency is optional
if (refs != null)
{
int max = 1;
boolean retval = true;
if (m_dependencyMetadata.isMultiple() == true)
{
max = refs.length;
}
for (int index = 0; index < max; index++)
{
retval = invokeBindMethod(m_implementationObject, refs[index]);
if(retval == false && (max == 1))
{
// There was an exception when calling the bind method
Activator.error("Dependency Manager: Possible exception in the bind method during initialize()");
m_isValid = false;
//setStateDependency(DependencyChangeEvent.DEPENDENCY_INVALID);
return m_isValid;
}
}
}
return m_isValid;
}
/**
* Revoke all bindings. This method cannot throw an exception since it must try
* to complete all that it can
*
**/
private void unbind()
{
Object []allrefs = m_boundServicesRefs.toArray();
if (allrefs == null)
return;
for (int i = 0; i < allrefs.length; i++)
{
invokeUnbindMethod(m_implementationObject, (ServiceReference)allrefs[i]);
}
}
/**
*
* Returns an array containing the service references that are pertinent to the
* dependency managed by this object. This method filters out services that
* belong to bundles that are being (or are actually) shutdown. This is an issue
* since is not clearly specified in the OSGi specification if a getServiceReference
* call should return the services that belong to bundles that are stopping.
*
* @return an array of ServiceReferences valid in the context of this dependency
**/
private ServiceReference [] getRequiredServiceRefs()
{
try
{
ArrayList list=new ArrayList();
ServiceReference temprefs[] = m_activator.getBundleContext().getServiceReferences(m_dependencyMetadata.getInterface(), m_dependencyMetadata.getTarget());
if (temprefs == null)
{
return null;
}
for (int i = 0; i < temprefs.length; i++)
{
if (temprefs[i].getBundle().getState() == Bundle.ACTIVE
|| temprefs[i].getBundle().getState() == Bundle.STARTING)
{
list.add(temprefs[i]);
}
}
return (ServiceReference []) list.toArray(new ServiceReference [temprefs.length]);
}
catch (Exception e)
{
Activator.error("DependencyManager: exception while getting references :"+e);
return null;
}
}
/**
* Gets a bind or unbind method according to the policies described in the specification
*
* @param methodname The name of the method
* @param targetClass the class to which the method belongs to
* @param parameterClassName the name of the class of the parameter that is passed to the method
* @return the method or null
* @throws java.lang.ClassNotFoundException if the class was not found
**/
private Method getBindingMethod(String methodname, Class targetClass, String parameterClassName)
{
Method method = null;
Class parameterClass = null;
// 112.3.1 The method is searched for using the following priority
// 1. The method's parameter type is org.osgi.framework.ServiceReference
// 2. The method's parameter type is the type specified by the reference's interface attribute
// 3. The method's parameter type is assignable from the type specified by the reference's interface attribute
try{
// Case 1
method = getMethod(targetClass, methodname, new Class[]{ServiceReference.class});
m_bindUsesServiceReference = true;
}
catch(NoSuchMethodException ex){
try {
// Case2
m_bindUsesServiceReference = false;
parameterClass = m_activator.getBundleContext().getBundle().loadClass(parameterClassName);
method = getMethod(targetClass, methodname, new Class[]{parameterClass});
}
catch(NoSuchMethodException ex2) {
// Case 3
method = null;
// iterate on class hierarchy
for ( ; method == null && targetClass != null; targetClass = targetClass.getSuperclass())
{
// Get all potential bind methods
Method candidateBindMethods[] = targetClass.getDeclaredMethods();
// Iterate over them
for(int i = 0; method == null && i < candidateBindMethods.length; i++) {
Method currentMethod = candidateBindMethods[i];
// Get the parameters for the current method
Class[] parameters = currentMethod.getParameterTypes();
// Select only the methods that receive a single parameter
// and a matching name
if(parameters.length == 1 && currentMethod.getName().equals(methodname)) {
// Get the parameter type
Class theParameter = parameters[0];
// Check if the parameter type is assignable from the type specified by the reference's interface attribute
if(theParameter.isAssignableFrom(parameterClass)) {
// Final check: it must be public or protected
if (Modifier.isPublic(method.getModifiers()) || Modifier.isProtected(method.getModifiers()))
{
if (!method.isAccessible())
{
method.setAccessible(true);
}
method = currentMethod;
}
}
}
}
}
}
catch(ClassNotFoundException ex2) {
Activator.exception("Cannot load class used as parameter "+parameterClassName,m_componentMetadata,ex2);
}
}
return method;
}
/**
* Call the bind method. In case there is an exception while calling the bind method, the service
* is not considered to be bound to the instance object
*
* @param implementationObject The object to which the service is bound
* @param ref A ServiceReference with the service that will be bound to the instance object
* @param storeRef A boolean that indicates if the reference must be stored (this is used for the delayed components)
* @return true if the call was successful, false otherwise
**/
private boolean invokeBindMethod(Object implementationObject, ServiceReference ref) {
// The bind method is only invoked if the implementation object is not null. This is valid
// for both immediate and delayed components
if(implementationObject != null) {
try {
// Get the bind method
Method bindMethod = getBindingMethod(m_dependencyMetadata.getBind(), implementationObject.getClass(), m_dependencyMetadata.getInterface());
if(bindMethod == null){
// 112.3.1 If the method is not found , SCR must log an error
// message with the log service, if present, and ignore the method
// TODO: log error message
Activator.trace("bind() method not found", m_componentMetadata);
return false;
}
// Get the parameter
Object parameter;
if(m_bindUsesServiceReference == false) {
parameter = m_activator.getBundleContext().getService(ref);
}
else {
parameter = ref;
}
// Invoke the method
bindMethod.invoke(implementationObject, new Object[] {parameter});
// Store the reference
m_boundServicesRefs.add(ref);
return true;
}
catch(IllegalAccessException ex)
{
// 112.3.1 If the method is not is not declared protected or public, SCR must log an error
// message with the log service, if present, and ignore the method
// TODO: log error message
return false;
}
catch(InvocationTargetException ex)
{
Activator.exception("DependencyManager : exception while invoking "+m_dependencyMetadata.getBind()+"()", m_componentMetadata, ex);
return false;
}
} else if( implementationObject == null && m_componentMetadata.isImmediate() == false) {
// In the case the implementation object is null and the component is delayed
// then we still have to store the object that is passed to the bind methods
// so that it can be used once the implementation object is created.
m_boundServicesRefs.add(ref);
return true;
} else {
// TODO: assert false : this theoretically never happens...
return false;
}
}
/**
* Call the unbind method
*
* @param implementationObject The object from which the service is unbound
* @param ref A service reference corresponding to the service that will be unbound
* @return true if the call was successful, false otherwise
**/
private boolean invokeUnbindMethod(Object implementationObject, ServiceReference ref) {
// TODO: assert m_boundServices.contains(ref) == true : "DependencyManager : callUnbindMethod UNBINDING UNKNOWN SERVICE !!!!";
// The unbind method is only invoked if the implementation object is not null. This is valid
// for both immediate and delayed components
if ( implementationObject != null ) {
try
{
// TODO: me quede aqui por que el unbind method no funciona
Activator.trace("getting unbind: "+m_dependencyMetadata.getUnbind(), m_componentMetadata);
Method unbindMethod = getBindingMethod(m_dependencyMetadata.getUnbind(), implementationObject.getClass(), m_dependencyMetadata.getInterface());
// Recover the object that is bound from the map.
//Object parameter = m_boundServices.get(ref);
Object parameter = null;
if(m_bindUsesServiceReference == true) {
parameter = ref;
} else {
parameter = m_activator.getBundleContext().getService(ref);
}
if(unbindMethod == null){
// 112.3.1 If the method is not found , SCR must log an error
// message with the log service, if present, and ignore the method
// TODO: log error message
Activator.trace("unbind() method not found", m_componentMetadata);
return false;
}
unbindMethod.invoke(implementationObject, new Object [] {parameter});
m_boundServicesRefs.remove(ref);
m_activator.getBundleContext().ungetService(ref);
return true;
}
catch (IllegalAccessException ex) {
// 112.3.1 If the method is not is not declared protected or public, SCR must log an error
// message with the log service, if present, and ignore the method
// TODO: log error message
return false;
}
catch (InvocationTargetException ex) {
Activator.exception("DependencyManager : exception while invoking "+m_dependencyMetadata.getUnbind()+"()", m_componentMetadata, ex);
return false;
}
} else if( implementationObject == null && m_componentMetadata.isImmediate() == false) {
// In the case the implementation object is null and the component is delayed
// then we still have to store the object that is passed to the bind methods
// so that it can be used once the implementation object is created.
m_boundServicesRefs.remove(ref);
return true;
} else {
// TODO: assert false : this theoretically never happens...
return false;
}
}
/**
* Called upon a service event. This method is responsible for calling the
* binding and unbinding methods and also to request the eventual unregistering
* of a service when a dependency breaks
*
* @param evt The ServiceEvent
**/
public void serviceChanged(ServiceEvent evt)
{
synchronized (ComponentManagerImpl.this)
{
// If the object is being created or destroyed, we can safely ignore events.
if (m_state == INSTANCE_DESTROYING || m_state == INSTANCE_DESTROYED || m_state == INSTANCE_CREATING || m_state == INSTANCE_CREATED)
{
return;
}
// If we are in the process of invalidating, it is not necessary to pass
// unregistration events, since we are unbinding everything anyway.
else if (m_state == INSTANCE_INVALIDATING && evt.getType() == ServiceEvent.UNREGISTERING)
{
return;
}
// We do not have an entry for VALIDATING because it is reentrant.
// A service is unregistering
if (evt.getType() == ServiceEvent.UNREGISTERING)
{
if (m_boundServicesRefs.contains(evt.getServiceReference()) == true)
{
// A static dependency is broken the instance manager will be invalidated
if (m_dependencyMetadata.isStatic())
{
m_isValid = false;
//setStateDependency(DependencyChangeEvent.DEPENDENCY_INVALID);
try
{
Activator.trace("Dependency Manager: Static dependency is broken", m_componentMetadata);
deactivate();
Activator.trace("Dependency Manager: RECREATING", m_componentMetadata);
activate();
}
catch(Exception ex)
{
Activator.exception("Exception while recreating dependency ",m_componentMetadata, ex);
}
}
// dynamic dependency
else
{
// Release references to the service, call unbinder method
// and eventually request service unregistration
invokeUnbindMethod(m_implementationObject, evt.getServiceReference());
// The only thing we need to do here is check if we can reinitialize
// once the bound services becomes zero. This tries to repair dynamic
// 1..1 or rebind 0..1, since replacement services may be available.
// In the case of aggregates, this will only invalidate them since they
// can't be repaired.
if (m_boundServicesRefs.size() == 0)
{
// try to reinitialize
if (!bind())
{
if (!m_dependencyMetadata.isOptional())
{
Activator.trace("Dependency Manager: Mandatory dependency not fullfilled and no replacements available... unregistering service...", m_componentMetadata);
deactivate();
Activator.trace("Dependency Manager: Recreating", m_componentMetadata);
activate();
}
}
}
}
}
}
// A service is registering.
else if (evt.getType() == ServiceEvent.REGISTERED)
{
if (m_boundServicesRefs.contains(evt.getServiceReference()) == true)
{
// This is a duplicate
Activator.trace("DependencyManager : ignoring REGISTERED ServiceEvent (already bound)", m_componentMetadata);
}
else
{
m_isValid = true;
//setStateDependency(DependencyChangeEvent.DEPENDENCY_VALID);
// If the InstanceManager is invalid, a call to validate is made
// which will fix everything.
if (ComponentManagerImpl.this.m_state != INSTANCE_VALID)
{
activate();
}
// Otherwise, this checks for dynamic 0..1, 0..N, and 1..N it never
// checks for 1..1 dynamic which is done above by the validate()
else if (!m_dependencyMetadata.isStatic())
{
// For dependency that are aggregates, always bind the service
// Otherwise only bind if bind services is zero, which captures the 0..1 case
if (m_dependencyMetadata.isMultiple() || m_boundServicesRefs.size() == 0)
{
invokeBindMethod(m_implementationObject, evt.getServiceReference());
}
}
}
}
}
}
}
/**
* Implementation for the ComponentContext interface
*
*/
class ComponentContextImpl implements ComponentContext {
private Bundle m_usingBundle;
ComponentContextImpl(Bundle usingBundle)
{
m_usingBundle = usingBundle;
}
public Dictionary getProperties() {
//TODO: 112.11.3.5 The Dictionary is read-only and cannot be modified
return m_componentMetadata.getProperties();
}
public Object locateService(String name) {
DependencyManager dm = getDependencyManager(name);
if (dm == null || dm.m_boundServicesRefs.isEmpty())
{
return null;
}
ServiceReference selectedRef;
if (dm.m_boundServicesRefs.size() == 1)
{
// short cut for single bound service
selectedRef = (ServiceReference) dm.m_boundServicesRefs.iterator().next();
}
else
{
// is it correct to assume an ordered bound services set ?
int maxRanking = Integer.MIN_VALUE;
long minId = Long.MAX_VALUE;
selectedRef = null;
Iterator it = dm.m_boundServicesRefs.iterator();
while (it.hasNext())
{
ServiceReference ref = (ServiceReference) it.next();
Integer rank = (Integer) ref.getProperty(Constants.SERVICE_RANKING);
int ranking = (rank == null) ? Integer.MIN_VALUE : rank.intValue();
long id = ((Long) ref.getProperty(Constants.SERVICE_ID)).longValue();
if (maxRanking < ranking || (maxRanking == ranking && id < minId))
{
maxRanking = ranking;
minId = id;
selectedRef = ref;
}
}
}
// this is not realistic, as at least one service is available
// whose service id is smaller than Long.MAX_VALUE, still be sure
if (selectedRef == null)
{
return null;
}
// return the service for the selected reference
return getBundleContext().getService(selectedRef);
}
public Object locateService(String name, ServiceReference ref) {
DependencyManager dm = getDependencyManager(name);
if (dm == null || dm.m_boundServicesRefs.isEmpty())
{
return null;
}
// is it correct to assume an ordered bound services set ?
Iterator it = dm.m_boundServicesRefs.iterator();
while (it.hasNext())
{
if (it.next().equals(ref))
{
return getBundleContext().getService(ref);
}
}
// no matching name and service reference found
return null;
}
public Object[] locateServices(String name) {
DependencyManager dm = getDependencyManager(name);
if (dm == null || dm.m_boundServicesRefs.isEmpty())
{
return null;
}
Object[] services = new Object[dm.m_boundServicesRefs.size()];
Iterator it = dm.m_boundServicesRefs.iterator();
for (int i=0; i < services.length && it.hasNext(); i++)
{
ServiceReference ref = (ServiceReference) it.next();
services[i] = getBundleContext().getService(ref);
}
return services;
}
private DependencyManager getDependencyManager(String name) {
Iterator it = m_dependencyManagers.iterator();
while (it.hasNext())
{
DependencyManager dm = (DependencyManager)it.next();
// if any of the dependency managers is unable to bind (it is invalid), the component is deactivated
if (name.equals(dm.m_dependencyMetadata.getName()))
{
return dm;
}
}
// not found
return null;
}
public BundleContext getBundleContext() {
return m_activator.getBundleContext();
}
public Bundle getUsingBundle() {
return m_usingBundle;
}
public ComponentInstance getComponentInstance() {
return ComponentManagerImpl.this;
}
public void enableComponent(String name) {
m_activator.enableComponent(name);
}
public void disableComponent(String name) {
m_activator.disableComponent(name);
}
public ServiceReference getServiceReference() {
if(m_serviceRegistration != null) {
return m_serviceRegistration.getReference();
}
else {
return null;
}
}
}
/**
* This class is a ServiceFactory that is used when a delayed component is created.
* This class returns the same service object instance for all bundles.
*
*/
class DelayedComponentServiceFactory implements ServiceFactory {
public Object getService(Bundle bundle, ServiceRegistration registration) {
Activator.trace("DelayedComponentServiceFactory.getService()", m_componentMetadata);
// When the getServiceMethod is called, the implementation object must be created
// unless another bundle has already retrievd it
if (m_implementationObject == null) {
m_componentContext = new ComponentContextImpl(null);
m_implementationObject = createImplementationObject( m_componentContext );
}
return m_implementationObject;
}
public void ungetService(Bundle bundle, ServiceRegistration registration, Object object) {
// nothing to do here, delayed components are deactivated when
// the component is deactivated and not when any bundle releases
// the service
}
protected Object createImplementationObject(ComponentContext componentContext) {
Object implementationObject;
// 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
try
{
// 112.4.4 The class is retrieved with the loadClass method of the component's bundle
Class c = m_activator.getBundleContext().getBundle().loadClass(m_componentMetadata.getImplementationClassName());
// 112.4.4 The class must be public and have a public constructor without arguments so component instances
// may be created by the SCR with the newInstance method on Class
implementationObject = c.newInstance();
}
catch (Exception ex)
{
// TODO: manage this exception when implementation object cannot be created
Activator.exception("Error during instantiation of the implementation object",m_componentMetadata,ex);
deactivate();
//invalidate();
return null;
}
// 3. Bind the target services
Iterator it = m_dependencyManagers.iterator();
while ( it.hasNext() )
{
DependencyManager dm = (DependencyManager)it.next();
Iterator bound = dm.m_boundServicesRefs.iterator();
while ( bound.hasNext() ) {
ServiceReference nextRef = (ServiceReference) bound.next();
dm.invokeBindMethod(implementationObject, nextRef);
}
}
// 4. Call the activate method, if present
// Search for the activate method
try {
Method activateMethod = getMethod(implementationObject.getClass(), "activate", new Class[]{ComponentContext.class});
activateMethod.invoke(implementationObject, new Object[]{componentContext});
}
catch(NoSuchMethodException ex) {
// We can safely ignore this one
Activator.trace("activate() method is not implemented", m_componentMetadata);
}
catch(IllegalAccessException ex) {
// Ignored, but should it be logged?
Activator.trace("activate() method cannot be called", m_componentMetadata);
}
catch(InvocationTargetException ex) {
// TODO: 112.5.8 If the activate method throws an exception, SCR must log an error message
// containing the exception with the Log Service
Activator.exception("The activate method has thrown an exception", m_componentMetadata, ex);
}
return implementationObject;
}
}
/**
* This class is a ServiceFactory that is used when a delayed component is created
* for a service factory service
*
*/
class DelayedServiceFactoryServiceFactory extends DelayedComponentServiceFactory
{
// we do not have to maintain references to the actual service
// instances as those are handled by the ServiceManager and given
// to the ungetService method when the bundle releases the service
// maintain the map of componentContext objects created for the
// service instances
private IdentityHashMap componentContexts = new IdentityHashMap();
public Object getService( Bundle bundle, ServiceRegistration registration )
{
Activator.trace( "DelayedServiceFactoryServiceFactory.getService()", m_componentMetadata );
// When the getServiceMethod is called, the implementation object must be created
// private ComponentContext and implementation instances
ComponentContext componentContext = new ComponentContextImpl( bundle );
Object implementationObject = createImplementationObject( componentContext );
// register the components component context
componentContexts.put( implementationObject, componentContext );
return implementationObject;
}
public void ungetService( Bundle bundle, ServiceRegistration registration, Object implementationObject )
{
Activator.trace( "DelayedServiceFactoryServiceFactory.ungetService()", m_componentMetadata );
// When the ungetServiceMethod is called, the implementation object must be deactivated
// private ComponentContext and implementation instances
ComponentContext componentContext = ( ComponentContext ) componentContexts.remove( implementationObject );
deactivateImplementationObject( implementationObject, componentContext );
}
protected void deactivateImplementationObject( Object implementationObject, ComponentContext componentContext )
{
// 1. Call the deactivate method, if present
// Search for the activate method
try
{
Method deactivateMethod = getMethod( implementationObject.getClass(), "deactivate", new Class[]
{ ComponentContext.class } );
deactivateMethod.invoke( implementationObject, new Object[]
{ componentContext } );
}
catch ( NoSuchMethodException ex )
{
// We can safely ignore this one
Activator.trace( "deactivate() method is not implemented", m_componentMetadata );
}
catch ( IllegalAccessException ex )
{
// Ignored, but should it be logged?
Activator.trace( "deactivate() method cannot be called", m_componentMetadata );
}
catch ( InvocationTargetException ex )
{
// TODO: 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
Activator.exception( "The deactivate method has thrown an exception", m_componentMetadata, ex );
}
// 2. Unbind any bound services
Iterator it = m_dependencyManagers.iterator();
while ( it.hasNext() )
{
DependencyManager dm = ( DependencyManager ) it.next();
Iterator bound = dm.m_boundServicesRefs.iterator();
while ( bound.hasNext() )
{
ServiceReference nextRef = ( ServiceReference ) bound.next();
dm.invokeUnbindMethod( implementationObject, nextRef );
}
}
// 3. Release all references
// nothing to do, we keep no references on per-Bundle services
}
}
/**
* 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.
*/
private 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);
}
}