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