| /* |
| * 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.lang.reflect.InvocationTargetException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Dictionary; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.felix.scr.impl.inject.ComponentMethods; |
| import org.apache.felix.scr.impl.logger.ComponentLogger; |
| import org.apache.felix.scr.impl.metadata.ComponentMetadata; |
| import org.apache.felix.scr.impl.metadata.ServiceMetadata.Scope; |
| import org.apache.felix.scr.impl.metadata.TargetedPID; |
| import org.osgi.framework.Constants; |
| import org.osgi.service.component.ComponentConstants; |
| import org.osgi.service.log.LogService; |
| import org.osgi.util.promise.Deferred; |
| import org.osgi.util.promise.Promise; |
| import org.osgi.util.promise.Promises; |
| |
| |
| /** |
| * The <code>ConfigurableComponentHolder</code> class is a |
| * {@link ComponentHolder} for automatically configured components instances |
| * that may or may not be configured through Config Admin. |
| * <p> |
| * The holder copes with three situations: |
| * <ul> |
| * <li>No configuration is available for the held component. That is there is |
| * no configuration whose <code>service.pid</code> or |
| * <code>service.factoryPid</code> equals the component name.</li> |
| * <li>A singleton configuration is available whose <code>service.pid</code> |
| * equals the component name.</li> |
| * <li>One or more factory configurations exist whose |
| * <code>service.factoryPid</code> equals the component name.</li> |
| * </ul> |
| */ |
| public abstract class ConfigurableComponentHolder<S> implements ComponentHolder<S>, ComponentContainer<S> |
| { |
| |
| /** |
| * The activator owning the per-bundle components |
| */ |
| private final ComponentActivator m_activator; |
| |
| /** |
| * The {@link ComponentMetadata} describing the held component(s) |
| */ |
| private final ComponentMetadata m_componentMetadata; |
| |
| /** the targeted pids corresponding to the pids specified in the config metadata, except possibly for the single |
| * factory pid |
| */ |
| private final TargetedPID[] m_targetedPids; |
| |
| private final Long[] m_changeCount; |
| |
| private final Map<String, Long> m_factoryChangeCount = new HashMap<>(); |
| |
| /** |
| * the index in metadata.getConfigurationPid() of the base factory pid, if any. Each component created from a factory configuration |
| * might have a different targeted pid. |
| */ |
| private volatile Integer m_factoryPidIndex; |
| |
| /** |
| * the non-factory configurations shared between all instances. |
| */ |
| private final Dictionary<String, Object>[] m_configurations; |
| |
| /** |
| * the factory configurations indexed by pid (which cannot be a TargetedPID since it's generated by CA). We have to track these since |
| * other required configs may not yet be present so we can't create the component manager yet. |
| */ |
| private final Map<String, Dictionary<String, Object>> m_factoryConfigurations = new HashMap<>(); |
| |
| /** |
| * Each factory config may be from a different TargetedPID (sharing the same base service pid, but with different level of detail) |
| */ |
| private final Map<String, TargetedPID> m_factoryTargetedPids = new HashMap<>(); |
| /** |
| * A map of components configured with factory configuration. The indices |
| * are the PIDs (<code>service.pid</code>) of the configuration objects. |
| * The values are the {@link SingleComponentManager<S> component instances} |
| * created on behalf of the configurations. |
| */ |
| private final Map<String, AbstractComponentManager<S>> m_components; |
| |
| /** |
| * The special component used if there is no configuration or a singleton |
| * configuration. This field is only <code>null</code> once all components |
| * held by this holder have been disposed of by |
| * {@link #disposeComponents(int)} and is first created in the constructor. |
| * As factory configurations are provided this instance may be configured |
| * or "deconfigured". |
| * <p> |
| * Expected invariants: |
| * <ul> |
| * <li>This field is only <code>null</code> after disposal of all held |
| * components</li> |
| * <li>The {@link #m_components} map is empty or the component pointed to |
| * by this field is also contained in the map</li> |
| * <ul> |
| */ |
| private volatile AbstractComponentManager<S> m_singleComponent; |
| |
| /** |
| * Whether components have already been enabled by calling the |
| * {@link #enableComponents(boolean)} method. If this field is <code>true</code> |
| * component instances created per configuration by the |
| * {@link #configurationUpdated(TargetedPID, TargetedPID, Dictionary, long)} method are also |
| * enabled. Otherwise they are not enabled immediately. |
| */ |
| private volatile boolean m_enabled; |
| private final Object enableLock = new Object(); |
| private volatile Promise<Void> m_enablePromise; |
| private volatile Promise<Void> m_disablePromise = Promises.resolved(null); |
| |
| private final ComponentMethods<S> m_componentMethods; |
| |
| private final ComponentLogger logger; |
| |
| public ConfigurableComponentHolder( final ComponentActivator activator, |
| final ComponentMetadata metadata, |
| final ComponentLogger logger) |
| { |
| this.logger = logger; |
| this.m_activator = activator; |
| this.m_componentMetadata = metadata; |
| final int pidCount = metadata.getConfigurationPid().size(); |
| this.m_targetedPids = new TargetedPID[pidCount]; |
| this.m_configurations = new Dictionary[pidCount]; |
| this.m_changeCount = new Long[pidCount]; |
| this.m_components = new HashMap<>(); |
| this.m_componentMethods = createComponentMethods(); |
| this.m_enabled = false; |
| } |
| |
| protected abstract ComponentMethods<S> createComponentMethods(); |
| |
| protected ComponentMethods<S> getComponentMethods() { |
| return m_componentMethods; |
| } |
| |
| protected AbstractComponentManager<S> createComponentManager(boolean factoryConfiguration) |
| { |
| |
| AbstractComponentManager<S> manager; |
| if ( m_componentMetadata.isFactory() ) |
| { |
| //TODO is there any check to make sure factory component factories are enabled before creating them? |
| if ( !m_componentMetadata.isObsoleteFactoryComponentFactory() || !factoryConfiguration ) |
| { |
| manager = new ComponentFactoryImpl<>(this, m_componentMethods); |
| } |
| else |
| { |
| manager = new SingleComponentManager<>(this, m_componentMethods, true ); |
| } |
| } |
| else if ( m_componentMetadata.getServiceScope() == Scope.bundle ) |
| { |
| manager = new ServiceFactoryComponentManager<>( this, m_componentMethods ); |
| } |
| |
| else if ( m_componentMetadata.getServiceScope() == Scope.prototype ) |
| { |
| manager = PSFLoader.newPSFComponentManager(this, m_componentMethods); |
| } |
| |
| else |
| { |
| //immediate or delayed |
| manager = new SingleComponentManager<>( this, m_componentMethods ); |
| } |
| |
| return manager; |
| } |
| |
| private static class PSFLoader |
| { |
| static <S> AbstractComponentManager<S> newPSFComponentManager(ConfigurableComponentHolder<S> holder, ComponentMethods methods) |
| { |
| return new PrototypeServiceFactoryComponentManager<>( holder, methods ); |
| } |
| } |
| |
| |
| @Override |
| public final ComponentActivator getActivator() |
| { |
| return m_activator; |
| } |
| |
| |
| @Override |
| public final ComponentMetadata getComponentMetadata() |
| { |
| return m_componentMetadata; |
| } |
| |
| |
| /** |
| * The configuration with the given <code>pid</code> |
| * (<code>service.pid</code> of the configuration object) is deleted. |
| * <p> |
| * The following situations are supported: |
| * <ul> |
| * <li>The configuration was a singleton configuration (pid equals the |
| * component name). In this case the internal component map is empty and |
| * the single component has been configured by the singleton configuration |
| * and is no "deconfigured".</li> |
| * <li>A factory configuration object has been deleted and the configured |
| * object is set as the single component. If the single component held the |
| * last factory configuration object, it is deconfigured. Otherwise the |
| * single component is disposed off and replaced by another component in |
| * the map of existing components.</li> |
| * <li>A factory configuration object has been deleted and the configured |
| * object is not set as the single component. In this case the component is |
| * simply disposed off and removed from the internal map.</li> |
| * </ul> |
| */ |
| @Override |
| public void configurationDeleted( final TargetedPID pid, TargetedPID factoryPid ) |
| { |
| logger.log( LogService.LOG_DEBUG, "ImmediateComponentHolder configuration deleted for pid {0}", null, pid); |
| |
| // component to deconfigure or dispose of |
| final Map<AbstractComponentManager<S>, Map<String, Object>> scms = new HashMap<>(); |
| boolean reconfigure = false; |
| |
| synchronized ( m_components ) |
| { |
| if (factoryPid != null) { |
| checkFactoryPidIndex(factoryPid); |
| String servicePid = pid.getServicePid(); |
| m_factoryTargetedPids.remove(servicePid); |
| m_factoryChangeCount.remove(servicePid); |
| m_factoryConfigurations.remove(servicePid); |
| AbstractComponentManager<S> scm = m_components.remove(servicePid); |
| if ( m_factoryConfigurations.isEmpty() ) |
| { |
| m_factoryPidIndex = null; |
| } |
| if ( !m_enabled || scm == null ) |
| { |
| return; |
| } |
| reconfigure = m_componentMetadata.isConfigurationOptional() && m_components.isEmpty(); |
| if ( reconfigure ) |
| { |
| m_singleComponent = scm; |
| scms.put( scm, mergeProperties(null) ); |
| } |
| else |
| { |
| scms.put( scm, null ); |
| } |
| } |
| else |
| { |
| //singleton pid |
| int index = getSingletonPidIndex(pid); |
| m_targetedPids[index] = null; |
| m_changeCount[index] = null; |
| m_configurations[index] = null; |
| if ( !m_enabled ) |
| { |
| return; |
| } |
| reconfigure = m_componentMetadata.isConfigurationOptional(); |
| |
| if ( m_factoryPidIndex == null) |
| { |
| if ( m_singleComponent != null ) { |
| if (reconfigure) { |
| scms.put(m_singleComponent, mergeProperties(null)); |
| } else { |
| scms.put(m_singleComponent, null); |
| m_singleComponent = null; |
| } |
| } |
| } |
| else |
| { |
| if (reconfigure) { |
| for (Map.Entry<String, AbstractComponentManager<S>> entry : m_components.entrySet()) { |
| scms.put(entry.getValue(), mergeProperties(entry.getKey())); |
| } |
| } |
| else |
| { |
| for (Map.Entry<String, AbstractComponentManager<S>> entry : m_components.entrySet()) { |
| scms.put(entry.getValue(), null ); |
| } |
| m_components.clear(); |
| } |
| } |
| |
| } |
| } |
| |
| for ( Map.Entry<AbstractComponentManager<S>,Map<String, Object>> entry: scms.entrySet()) |
| { |
| if ( reconfigure ) { |
| entry.getKey().reconfigure( entry.getValue(), true, factoryPid); |
| } else { |
| entry.getKey().dispose(ComponentConstants.DEACTIVATION_REASON_CONFIGURATION_DELETED); |
| } |
| } |
| } |
| |
| |
| /** |
| * Configures a component with the given configuration. This configuration |
| * update may happen in various situations: |
| * <ul> |
| * <li>The <code>pid</code> equals the component name. Hence we have a |
| * singleton configuration for the single component held by this holder</li> |
| * <li>The configuration is a factory configuration and is the first |
| * configuration provided. In this case the single component is provided |
| * with the configuration and also stored in the map.</li> |
| * <li>The configuration is a factory configuration but not the first. In |
| * this case a new component is created, configured and stored in the map</li> |
| * </ul> |
| * @return true if a new configuration was created, false otherwise. |
| * |
| * TODO there are now 3 states..... still not satisfied, existing, and new |
| */ |
| @Override |
| public boolean configurationUpdated( TargetedPID pid, TargetedPID factoryPid, final Dictionary<String, Object> props, long changeCount ) |
| { |
| logger.log(LogService.LOG_DEBUG, |
| "ConfigurableComponentHolder configuration updated for pid {0} with change count {1}", null, pid, |
| changeCount); |
| |
| // component to update or create |
| final Map<AbstractComponentManager<S>, Map<String, Object>> scms = new HashMap<>(); |
| boolean created = false; |
| |
| synchronized (m_components) { |
| //Find or create the component manager, or return if not satisfied. |
| if (factoryPid != null) { |
| checkFactoryPidIndex(factoryPid); |
| Long oldChangeCount = m_factoryChangeCount.get(pid.getServicePid()); |
| TargetedPID oldTargetedPID = m_factoryTargetedPids.get(pid.getServicePid()); |
| if (oldChangeCount != null && changeCount <= oldChangeCount && factoryPid.equals(oldTargetedPID)) { |
| return false; |
| } |
| m_factoryChangeCount.put(pid.getServicePid(), changeCount); |
| m_factoryConfigurations.put(pid.getServicePid(), props); |
| m_factoryTargetedPids.put(pid.getServicePid(), factoryPid); |
| if (m_enabled && isSatisfied()) { |
| if (m_singleComponent != null && !m_componentMetadata.isObsoleteFactoryComponentFactory()) { |
| AbstractComponentManager<S> scm = m_singleComponent; |
| scms.put( scm, mergeProperties( pid.getServicePid() ) ); |
| m_singleComponent = null; |
| m_components.put(pid.getServicePid(), scm); |
| } else if (m_components.containsKey(pid.getServicePid())) { |
| scms.put( m_components.get(pid.getServicePid()), mergeProperties( pid.getServicePid()) ); |
| } else { |
| AbstractComponentManager<S> scm = createComponentManager(true); |
| m_components.put(pid.getServicePid(), scm); |
| scms.put( scm, mergeProperties( pid.getServicePid()) ); |
| created = true; |
| } |
| } else { |
| return false; |
| } |
| |
| } else { |
| //singleton pid |
| int index = getSingletonPidIndex(pid); |
| if (m_changeCount[index] != null && changeCount <= m_changeCount[index] && pid.equals(m_targetedPids[index])) { |
| return false; |
| } |
| m_changeCount[index] = changeCount; |
| m_targetedPids[index] = pid; |
| m_configurations[index] = props; |
| if (m_enabled && isSatisfied()) { |
| if (m_singleComponent != null) { |
| scms.put( m_singleComponent, mergeProperties( pid.getServicePid() ) ); |
| } |
| else if ( m_factoryPidIndex != null) |
| { |
| for (Map.Entry<String, AbstractComponentManager<S>> entry: m_components.entrySet()) |
| { |
| scms.put(entry.getValue(), mergeProperties( entry.getKey())); |
| } |
| } |
| else |
| { |
| m_singleComponent = createComponentManager(false); |
| scms.put( m_singleComponent, mergeProperties( pid.getServicePid() ) ); |
| created = true; |
| } |
| } else { |
| return false; |
| } |
| |
| } |
| |
| } |
| |
| |
| // we have the icm. |
| //properties is all the configs merged together (without any possible component factory info. |
| |
| final boolean enable = created && m_enabled;// TODO WTF?? && getComponentMetadata().isEnabled(); |
| for ( Map.Entry<AbstractComponentManager<S>,Map<String, Object>> entry: scms.entrySet()) |
| { |
| // configure the component |
| entry.getKey().reconfigure(entry.getValue(), false, factoryPid); |
| logger.log(LogService.LOG_DEBUG, |
| "ImmediateComponentHolder Finished configuring the dependency managers for component for pid {0} ", null, |
| pid ); |
| if (enable) { |
| entry.getKey().enable(false); |
| logger.log(LogService.LOG_DEBUG, |
| "ImmediateComponentHolder Finished enabling component for pid {0} ", null, |
| pid ); |
| } else { |
| logger.log(LogService.LOG_DEBUG, |
| "ImmediateComponentHolder Will not enable component for pid {0}: holder enabled state: {1}, metadata enabled: {2} ", null, |
| pid, m_enabled, m_componentMetadata.isEnabled()); |
| } |
| } |
| return created; |
| } |
| |
| private Map<String, Object> mergeProperties(String servicePid) { |
| Map<String, Object> properties = new HashMap<>(m_componentMetadata.getProperties()); |
| List<String> pids = null; |
| boolean isDS13 = m_componentMetadata.getDSVersion().isDS13(); |
| if (isDS13) |
| { |
| pids = new ArrayList<>(); |
| if (properties.get(Constants.SERVICE_PID) instanceof String) |
| { |
| pids.add((String) properties.get(Constants.SERVICE_PID)); |
| } |
| } |
| for (int i = 0; i < m_configurations.length; i++) |
| { |
| if ( m_factoryPidIndex != null && i == m_factoryPidIndex |
| && !( m_componentMetadata.isObsoleteFactoryComponentFactory() && servicePid == null)) //obsolete special case |
| { |
| copyTo(properties, m_factoryConfigurations.get(servicePid)); |
| if (isDS13) |
| { |
| pids.add((String) m_factoryConfigurations.get(servicePid).get(Constants.SERVICE_PID)); |
| } |
| } |
| else if ( m_configurations[i] != null ) |
| { |
| copyTo(properties, m_configurations[i]); |
| if (isDS13) |
| { |
| pids.add((String) m_configurations[i].get(Constants.SERVICE_PID)); |
| } |
| } |
| } |
| if (isDS13 && !pids.isEmpty()) |
| { |
| if ( pids.size() == 1 ) |
| { |
| properties.put(Constants.SERVICE_PID, pids.get(0)); |
| } |
| else |
| { |
| properties.put(Constants.SERVICE_PID, pids); |
| } |
| } |
| return properties; |
| } |
| |
| private int getSingletonPidIndex(TargetedPID pid) { |
| int index = m_componentMetadata.getPidIndex(pid); |
| if (index == -1) { |
| logger.log(LogService.LOG_ERROR, |
| "Unrecognized pid {0}, expected one of {1}", null, |
| pid, |
| m_componentMetadata.getConfigurationPid() ); |
| throw new IllegalArgumentException("Unrecognized pid " |
| + pid); |
| } |
| if (m_factoryPidIndex != null && index == m_factoryPidIndex) { |
| logger.log(LogService.LOG_ERROR, |
| "singleton pid {0} supplied, but matches an existing factory pid at index: {1}", |
| null, pid, m_factoryPidIndex ); |
| throw new IllegalStateException( |
| "Singleton pid supplied matching a previous factory pid " |
| + pid); |
| } |
| return index; |
| } |
| |
| //TODO update error messages so they make sense for deleting config too. |
| private void checkFactoryPidIndex(TargetedPID factoryPid) { |
| int index = m_componentMetadata.getPidIndex(factoryPid); |
| if (index == -1) { |
| logger.log(LogService.LOG_ERROR, |
| "Unrecognized factory pid {0}, expected one of {1}", null, |
| factoryPid, |
| m_componentMetadata.getConfigurationPid() ); |
| throw new IllegalArgumentException( |
| "Unrecognized factory pid " + factoryPid); |
| } |
| if (m_configurations[index] != null) { |
| logger.log(LogService.LOG_ERROR, |
| "factory pid {0}, but this pid is already supplied as a singleton: {1} at index {2}", null, |
| factoryPid, Arrays.asList(m_targetedPids), index); |
| throw new IllegalStateException( |
| "Factory pid supplied after all non-factory configurations supplied " |
| + factoryPid); |
| } |
| if (m_factoryPidIndex == null) { |
| m_factoryPidIndex = index; |
| } else if (index != m_factoryPidIndex) { |
| logger.log(LogService.LOG_ERROR, |
| "factory pid {0} supplied for index {1}, but a factory pid previously supplied at index {2}", null, |
| factoryPid, index, m_factoryPidIndex ); |
| throw new IllegalStateException( |
| "Factory pid supplied at wrong index " + factoryPid); |
| } |
| } |
| |
| protected static void copyTo( Map<String, Object> target, Dictionary<String, ?> source ) |
| { |
| |
| for ( Enumeration<String> keys = source.keys(); keys.hasMoreElements(); ) |
| { |
| String key = keys.nextElement(); |
| Object value = source.get(key); |
| target.put(key, value); |
| } |
| } |
| |
| /** |
| * Determine if the holder is satisfied with configurations |
| * @return true if configuration optional or all pids supplied with configurations |
| */ |
| private boolean isSatisfied() { |
| if ( m_componentMetadata.isConfigurationOptional() || m_componentMetadata.isConfigurationIgnored() ) |
| { |
| return true; |
| } |
| for ( int i = 0; i < m_componentMetadata.getConfigurationPid().size(); i++) |
| { |
| if ( m_configurations[i] != null) |
| { |
| continue; |
| } |
| if ( m_factoryPidIndex != null && m_factoryPidIndex == i) |
| { |
| continue; |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| @Override |
| public List<? extends ComponentManager<?>> getComponents() |
| { |
| synchronized ( m_components ) |
| { |
| return getComponentManagers( ); |
| } |
| } |
| |
| |
| @Override |
| public boolean isEnabled() { |
| return m_enabled; |
| } |
| |
| |
| private void wait(Promise<Void> promise) |
| { |
| boolean waited = false; |
| boolean interrupted = false; |
| while ( !waited ) |
| { |
| try |
| { |
| promise.getValue(); |
| waited = true; |
| } |
| catch ( InterruptedException e ) |
| { |
| interrupted = true; |
| } |
| catch (InvocationTargetException e) |
| { |
| //this is not going to happen |
| } |
| } |
| if ( interrupted ) |
| { |
| Thread.currentThread().interrupt(); |
| } |
| } |
| |
| @Override |
| public Promise<Void> enableComponents( final boolean async ) |
| { |
| synchronized (enableLock) |
| { |
| if ( m_enablePromise != null) |
| { |
| return m_enablePromise; |
| } |
| wait( m_disablePromise ); |
| |
| List<AbstractComponentManager<S>> cms = new ArrayList<>(); |
| synchronized ( m_components ) |
| { |
| if ( isSatisfied() ) |
| { |
| if ( m_factoryPidIndex == null || |
| (m_componentMetadata.isObsoleteFactoryComponentFactory() && !m_componentMetadata.isConfigurationRequired())) |
| { |
| m_singleComponent = createComponentManager(false); |
| cms.add( m_singleComponent ); |
| m_singleComponent.reconfigure(mergeProperties( null ), false, null); |
| } |
| if ( m_factoryPidIndex != null) |
| { |
| for (String pid: m_factoryConfigurations.keySet()) { |
| AbstractComponentManager<S> scm = createComponentManager(true); |
| m_components.put(pid, scm); |
| scm.reconfigure( mergeProperties( pid ), false, new TargetedPID(pid)); |
| cms.add( scm ); |
| } |
| } |
| } |
| m_enabled = true; |
| } |
| List<Promise<Void>> promises = new ArrayList<>(); |
| for ( AbstractComponentManager<S> cm : cms ) |
| { |
| promises.add(cm.enable( async )); |
| } |
| m_enablePromise = new Deferred<List<Void>>().resolveWith(Promises.<Void, Void>all(promises)); |
| m_disablePromise = null; |
| return m_enablePromise; |
| } |
| } |
| |
| |
| @Override |
| public Promise<Void> disableComponents( final boolean async ) |
| { |
| synchronized (enableLock) |
| { |
| if ( m_disablePromise != null) |
| { |
| return m_disablePromise; |
| } |
| wait( m_enablePromise ); |
| |
| List<AbstractComponentManager<S>> cms; |
| synchronized ( m_components ) |
| { |
| m_enabled = false; |
| |
| cms = getDirectComponentManagers( ); |
| clearComponents(); |
| } |
| List<Promise<Void>> promises = new ArrayList<>(); |
| for ( AbstractComponentManager<S> cm : cms ) |
| { |
| promises.add(cm.disable( async )); |
| } |
| m_disablePromise = new Deferred<List<Void>>().resolveWith(Promises.<Void, Void>all(promises)); |
| m_enablePromise = null; |
| return m_disablePromise; |
| } |
| } |
| |
| |
| @Override |
| public void disposeComponents( final int reason ) |
| { |
| List<AbstractComponentManager<S>> cms; |
| synchronized ( m_components ) |
| { |
| cms = getDirectComponentManagers( ); |
| clearComponents(); |
| } |
| for ( AbstractComponentManager<S> cm : cms ) |
| { |
| cm.dispose( reason ); |
| } |
| } |
| |
| |
| @Override |
| public void disposed( SingleComponentManager<S> component ) |
| { |
| // ensure the component is removed from the components map |
| synchronized ( m_components ) |
| { |
| if ( !m_components.isEmpty() ) |
| { |
| for ( Iterator<AbstractComponentManager<S>> vi = m_components.values().iterator(); vi.hasNext(); ) |
| { |
| if ( component == vi.next() ) |
| { |
| vi.remove(); |
| break; |
| } |
| } |
| } |
| |
| if ( component == m_singleComponent ) |
| { |
| m_singleComponent = null; |
| } |
| } |
| } |
| |
| /** |
| * Compares this {@code ImmediateComponentHolder} object to another object. |
| * |
| * <p> |
| * A ImmediateComponentHolder is considered to be <b>equal to </b> another |
| * ImmediateComponentHolder if the component names are equal(using |
| * {@code String.equals}) and they have the same bundle activator |
| * |
| * @param object The {@code ImmediateComponentHolder} object to be compared. |
| * @return {@code true} if {@code object} is a |
| * {@code ImmediateComponentHolder} and is equal to this object; |
| * {@code false} otherwise. |
| */ |
| @Override |
| public boolean equals(Object object) |
| { |
| if (!(object instanceof ConfigurableComponentHolder)) |
| { |
| return false; |
| } |
| |
| @SuppressWarnings("unchecked") |
| ConfigurableComponentHolder<S> other = (ConfigurableComponentHolder<S>) object; |
| return m_activator == other.m_activator |
| && getName().equals(other.getName()); |
| } |
| |
| /** |
| * Returns a hash code value for the object. |
| * |
| * @return An integer which is a hash code value for this object. |
| */ |
| @Override |
| public int hashCode() |
| { |
| return getName().hashCode(); |
| } |
| |
| @Override |
| public String toString() |
| { |
| return "[ImmediateComponentHolder:" + getName() + "]"; |
| } |
| |
| String getName() |
| { |
| return m_componentMetadata.getName(); |
| } |
| |
| //---------- internal |
| |
| /** |
| * Returns all component managers from the map and the single component manager, optionally also removing them |
| * from the map. If there are no component managers, <code>null</code> |
| * is returned. Must be called synchronized on m_components. |
| */ |
| List<AbstractComponentManager<S>> getComponentManagers( ) |
| { |
| List<AbstractComponentManager<S>> cms = new ArrayList<>(); |
| if ( m_singleComponent != null) |
| { |
| m_singleComponent.getComponentManagers(cms); |
| } |
| |
| for (AbstractComponentManager<S> cm: m_components.values()) |
| { |
| cm.getComponentManagers(cms); |
| } |
| return cms; |
| } |
| |
| List<AbstractComponentManager<S>> getDirectComponentManagers( ) |
| { |
| List<AbstractComponentManager<S>> cms = new ArrayList<>(); |
| if ( m_singleComponent != null) |
| { |
| cms.add(m_singleComponent); |
| } |
| cms.addAll(m_components.values()); |
| return cms; |
| } |
| |
| void clearComponents() |
| { |
| m_components.clear(); |
| m_singleComponent = null; |
| } |
| |
| |
| @Override |
| public ComponentLogger getLogger() { |
| return logger; |
| } |
| |
| @Override |
| public TargetedPID getConfigurationTargetedPID(TargetedPID pid, TargetedPID factoryPid) |
| { |
| if ( factoryPid == null ) |
| { |
| int index = m_componentMetadata.getPidIndex(pid); |
| if (index != -1) |
| { |
| return m_targetedPids[index]; |
| } |
| return null; |
| } |
| //each factory configured component may have a different factory targeted pid. |
| synchronized (m_components) |
| { |
| return m_factoryTargetedPids.get(pid.getServicePid()); |
| } |
| } |
| |
| } |