/* * 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.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.Component; 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 Component, ComponentInstance { // the ID of this component private long m_componentId; // The state of this instance manager // methods accessing this field should be synchronized unless there is a // good reason to not be synchronized 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 volatile 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. *

* The transition cases are listed below. *

*/ 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, ComponentRegistry componentRegistry ) { m_activator = activator; m_componentMetadata = metadata; m_componentId = componentRegistry.createComponentId(); 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. *

* This method ignores the enabled flag of the component metadata * and just enables as requested. *

* 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. *

* 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. *

* 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. *

* 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 ); } final ServiceReference getServiceReference() { // This method is not synchronized even though it accesses the state. // The reason for this is that we just want to have the state return // the service reference which comes from the service registration. // The only thing that may happen is that the service registration is // still set on this instance but the service has already been // unregistered. In this case an IllegalStateException may be thrown // which we just catch and ignore returning null State state = m_state; try { return state.getServiceReference( this ); } catch ( IllegalStateException ise ) { // may be thrown if the service has already been unregistered but // the service registration is still set on this component manager // we ignore this exception and assume there is no service reference } return null; } //---------- Component handling methods ---------------------------------- /** * Method is called by {@link #activate()} in STATE_ACTIVATING or by * {@link DelayedComponentManager#getService(Bundle, ServiceRegistration)} * in STATE_REGISTERED. * * @return true if creation of the component succeeded. If * false 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. *

* 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. *

* 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. *

* 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 source Dictionary * into the target Dictionary. * * @param target The Dictionary into which to copy the * properties. If null a new Hashtable is * created. * @param source The Dictionary providing the properties to * copy. If null or empty, nothing is copied. * * @return The target is returned, which may be empty if * source is null or empty and * target was null. */ 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; } }