| /* |
| * 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.IdentityHashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.felix.scr.component.ExtFactoryComponentInstance; |
| import org.apache.felix.scr.impl.BundleComponentActivator; |
| import org.apache.felix.scr.impl.inject.ComponentMethods; |
| import org.apache.felix.scr.impl.inject.RefPair; |
| import org.apache.felix.scr.impl.metadata.ComponentMetadata; |
| import org.apache.felix.scr.impl.metadata.ReferenceMetadata; |
| import org.apache.felix.scr.impl.metadata.TargetedPID; |
| import org.osgi.framework.Constants; |
| import org.osgi.service.component.ComponentConstants; |
| import org.osgi.service.component.ComponentException; |
| import org.osgi.service.component.ComponentFactory; |
| import org.osgi.service.component.ComponentInstance; |
| import org.osgi.service.log.LogService; |
| |
| /** |
| * The <code>ComponentFactoryImpl</code> extends the {@link AbstractComponentManager} |
| * class to implement the component factory functionality. As such the |
| * OSGi Declarative Services <code>ComponentFactory</code> interface is |
| * implemented. |
| * <p> |
| * In addition the {@link ComponentHolder} interface is implemented to use this |
| * class directly as the holder for component instances created by the |
| * {@link #newInstance(Dictionary)} method. |
| * <p> |
| * This class implements spec-compliant component factories and the felix |
| * "persistent" component factory, where the factory is always registered whether or |
| * not all dependencies are present and the created components also persist whether or |
| * not the dependencies are present to allow the component instance to exist. |
| */ |
| public class ComponentFactoryImpl<S> extends AbstractComponentManager<S> implements ComponentFactory<S>, ComponentContainer<S> |
| { |
| |
| /** |
| * Contains the component instances created by calling the |
| * {@link #newInstance(Dictionary)} method. These component instances are |
| * provided with updated configuration (or deleted configuration) if |
| * such modifications for the component factory takes place. |
| * <p> |
| * The map is keyed by the component manager instances. The value of each |
| * entry is the same as the entry's key. |
| * This is an IdentityHashMap for speed, thus not a Set. |
| */ |
| private final Map<SingleComponentManager<S>, SingleComponentManager<S>> m_componentInstances; |
| |
| /** |
| * The configuration for the component factory. This configuration is |
| * supplied as the base configuration for each component instance created |
| * by the {@link #newInstance(Dictionary)} method. |
| */ |
| private volatile Map<String, Object> m_configuration; |
| |
| /** |
| * Flag telling if our component factory is currently configured from config admin. |
| * We are configured when configuration policy is required and we have received the |
| * config admin properties, or when configuration policy is optional or ignored. |
| */ |
| private volatile boolean m_hasConfiguration; |
| |
| /** |
| * Configuration change count (R5) or imitation (R4) |
| */ |
| protected volatile long m_changeCount = -1; |
| |
| protected TargetedPID m_targetedPID; |
| |
| public ComponentFactoryImpl( ComponentContainer<S> container, ComponentMethods componentMethods ) |
| { |
| super( container, componentMethods ); |
| m_componentInstances = new IdentityHashMap<>(); |
| m_configuration = new HashMap<>(); |
| } |
| |
| |
| @Override |
| protected boolean verifyDependencyManagers() |
| { |
| if (!getComponentMetadata().isPersistentFactoryComponent()) |
| { |
| return super.verifyDependencyManagers(); |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean isFactory() |
| { |
| return true; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.osgi.service.component.ComponentFactory#newInstance(java.util.Dictionary) |
| */ |
| @Override |
| public ComponentInstance<S> newInstance( Dictionary<String, ?> dictionary ) |
| { |
| final SingleComponentManager<S> cm = createComponentManager(); |
| getLogger().log(LogService.LOG_DEBUG, "Creating new instance from component factory", null); |
| |
| cm.setFactoryProperties( dictionary ); |
| //configure the properties |
| cm.reconfigure( m_configuration, false, null ); |
| // enable |
| cm.enableInternal(); |
| |
| ComponentInstance<S> instance; |
| if ( getComponentMetadata().isPersistentFactoryComponent() ) |
| { |
| instance = new ModifyComponentInstance<>(cm); |
| } |
| else |
| { |
| instance = cm.getComponentInstance(); |
| if ( instance == null || instance.getInstance() == null ) |
| { |
| // activation failed, clean up component manager |
| cm.dispose( ComponentConstants.DEACTIVATION_REASON_DISPOSED ); |
| throw new ComponentException( "Failed activating component" ); |
| } |
| } |
| |
| synchronized ( m_componentInstances ) |
| { |
| m_componentInstances.put( cm, cm ); |
| } |
| |
| return instance; |
| } |
| |
| private static class ModifyComponentInstance<S> implements ExtFactoryComponentInstance<S> |
| { |
| private final SingleComponentManager<S> cm; |
| |
| public ModifyComponentInstance(SingleComponentManager<S> cm) |
| { |
| this.cm = cm; |
| } |
| |
| @Override |
| public void dispose() |
| { |
| cm.dispose(); |
| } |
| |
| @Override |
| public S getInstance() |
| { |
| final ComponentInstance<S> componentInstance = cm.getComponentInstance(); |
| return componentInstance == null? null: componentInstance.getInstance(); |
| } |
| |
| @Override |
| public void modify(Dictionary<String, ?> properties) |
| { |
| cm.setFactoryProperties( properties ); |
| cm.reconfigure(false); |
| } |
| |
| } |
| |
| /** |
| * Compares this {@code ComponentFactoryImpl} object to another object. |
| * |
| * <p> |
| * A component factory impl is considered to be <b>equal to </b> another component |
| * factory impl if the component names are equal(using {@code String.equals}). |
| * |
| * @param object The {@code ComponentFactoryImpl} object to be compared. |
| * @return {@code true} if {@code object} is a |
| * {@code ComponentFactoryImpl} and is equal to this object; |
| * {@code false} otherwise. |
| */ |
| @Override |
| public boolean equals(Object object) |
| { |
| if (!(object instanceof ComponentFactoryImpl<?>)) |
| { |
| return false; |
| } |
| |
| ComponentFactoryImpl<?> other = (ComponentFactoryImpl<?>) object; |
| return getComponentMetadata().getName().equals(other.getComponentMetadata().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 getComponentMetadata().getName().hashCode(); |
| } |
| |
| /** |
| * The component factory does not have a component to delete. |
| * <p> |
| * But in the backwards compatible case any instances created for factory |
| * configuration instances are to disabled as a consequence of deactivating |
| * the component factory. |
| */ |
| @Override |
| protected void deleteComponent( int reason ) |
| { |
| } |
| |
| |
| @Override |
| protected String[] getProvidedServices() |
| { |
| return new String[] { ComponentFactory.class.getName() }; |
| } |
| |
| |
| public boolean hasConfiguration() |
| { |
| return m_hasConfiguration; |
| } |
| |
| |
| /** |
| * For ComponentFactoryImpl, this is used only for updating targets on the dependency managers, so we don't need any other |
| * properties. |
| */ |
| @Override |
| public Map<String, Object> getProperties() |
| { |
| Map<String, Object> props = new HashMap<>(); |
| |
| // add target properties of references |
| List<ReferenceMetadata> depMetaData = getComponentMetadata().getDependencies(); |
| for ( ReferenceMetadata rm : depMetaData ) |
| { |
| if ( rm.getTarget() != null ) |
| { |
| props.put( rm.getTargetPropertyName(), rm.getTarget() ); |
| } |
| } |
| |
| // add target properties from configuration (if we have one) |
| for ( String key : m_configuration.keySet() ) |
| { |
| if ( key.endsWith( ".target" ) ) |
| { |
| props.put( key, m_configuration.get( key ) ); |
| } |
| } |
| |
| return props; |
| } |
| |
| @Override |
| public void setServiceProperties( Dictionary<String, ?> serviceProperties ) |
| { |
| throw new IllegalStateException( "ComponentFactory service properties are immutable" ); |
| } |
| |
| @Override |
| void postRegister() |
| { |
| //do nothing |
| } |
| |
| @Override |
| void preDeregister() |
| { |
| //do nothing |
| } |
| |
| @Override |
| public Dictionary<String, Object> getServiceProperties() |
| { |
| Dictionary<String, Object> props = new Hashtable<>(getComponentMetadata().getFactoryProperties()); |
| |
| // 112.5.5 The Component Factory service must register with the following properties |
| props.put( ComponentConstants.COMPONENT_NAME, getComponentMetadata().getName() ); |
| props.put( ComponentConstants.COMPONENT_FACTORY, getComponentMetadata().getFactoryIdentifier() ); |
| |
| props.put( Constants.SERVICE_VENDOR, "The Apache Software Foundation" ); |
| |
| return props; |
| } |
| |
| @Override |
| boolean hasInstance() |
| { |
| return false; |
| } |
| |
| @Override |
| protected boolean collectDependencies(ComponentContextImpl<S> componentContext) |
| { |
| return true; |
| } |
| |
| @Override |
| <T> boolean invokeUpdatedMethod( DependencyManager<S, T> dependencyManager, RefPair<S, T> ref, int trackingCount ) |
| { |
| return false; |
| } |
| |
| @Override |
| <T> void invokeBindMethod( DependencyManager<S, T> dependencyManager, RefPair<S, T> reference, int trackingCount ) |
| { |
| } |
| |
| @Override |
| <T> void invokeUnbindMethod( DependencyManager<S, T> dependencyManager, RefPair<S, T> oldRef, int trackingCount ) |
| { |
| } |
| |
| //---------- Component interface |
| |
| |
| /** |
| * Disposes off all components ever created by this component holder. This |
| * method is called if either the Declarative Services runtime is stopping |
| * or if the owning bundle is stopped. In both cases all components created |
| * by this holder must be disposed off. |
| */ |
| @Override |
| public void dispose( int reason ) |
| { |
| List<AbstractComponentManager<S>> cms = new ArrayList<>( ); |
| getComponentManagers( m_componentInstances, cms ); |
| for ( AbstractComponentManager<S> acm: cms ) |
| { |
| acm.dispose( reason ); |
| } |
| |
| synchronized ( m_componentInstances ) |
| { |
| m_componentInstances.clear(); |
| } |
| |
| // finally dispose the component factory itself |
| super.dispose( reason ); |
| } |
| |
| |
| @Override |
| public void disposed( SingleComponentManager<S> component ) |
| { |
| synchronized ( m_componentInstances ) |
| { |
| m_componentInstances.remove( component ); |
| } |
| } |
| |
| |
| //---------- internal |
| |
| |
| /** |
| * Creates an {@link SingleComponentManager} instance with the |
| * {@link BundleComponentActivator} and {@link ComponentMetadata} of this |
| * instance. The component manager is kept in the internal set of created |
| * components. The component is neither configured nor enabled. |
| */ |
| private SingleComponentManager<S> createComponentManager() |
| { |
| return new SingleComponentManager<>( this, getComponentMethods(), !getComponentMetadata().isPersistentFactoryComponent() ); |
| } |
| |
| |
| protected void getComponentManagers( Map<?, SingleComponentManager<S>> componentMap, List<AbstractComponentManager<S>> componentManagers ) |
| { |
| if ( componentMap != null ) |
| { |
| synchronized ( componentMap ) |
| { |
| componentManagers.addAll( componentMap.values() ); |
| } |
| } |
| } |
| |
| public TargetedPID getConfigurationTargetedPID(TargetedPID pid, TargetedPID factoryPid) |
| { |
| return m_targetedPID; |
| } |
| |
| |
| @Override |
| public void reconfigure(Map<String, Object> configuration, boolean configurationDeleted, TargetedPID factoryPid) { |
| if ( factoryPid != null ) { |
| // ignore factory configuration changes for component factories. |
| return; |
| } |
| m_configuration = configuration; |
| List<SingleComponentManager<S>> cms; |
| synchronized (m_componentInstances) |
| { |
| cms = new ArrayList<>(m_componentInstances.keySet()); |
| } |
| for (SingleComponentManager<S> cm: cms) |
| { |
| cm.reconfigure( configuration, configurationDeleted, factoryPid); |
| } |
| } |
| |
| |
| @Override |
| public void getComponentManagers(List<AbstractComponentManager<S>> cms) |
| { |
| synchronized (m_componentInstances) |
| { |
| cms.addAll(m_componentInstances.keySet()); |
| } |
| } |
| } |