Package org.apache.felix.scr.impl

Source Code of org.apache.felix.scr.impl.ComponentRegistry

/*
* 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.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.felix.scr.impl.config.ComponentHolder;
import org.apache.felix.scr.impl.config.ConfigurationSupport;
import org.apache.felix.scr.impl.config.ConfigurableComponentHolder;
import org.apache.felix.scr.impl.manager.AbstractComponentManager;
import org.apache.felix.scr.impl.manager.DependencyManager;
import org.apache.felix.scr.impl.metadata.ComponentMetadata;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.component.ComponentConstants;
import org.osgi.service.component.ComponentException;
import org.osgi.service.log.LogService;


/**
* The <code>ComponentRegistry</code> class acts as the global registry for
* components by name and by component ID. As such the component registry also
* registers itself as the {@link ScrService} to support access to the
* registered components.
*/
public class ComponentRegistry implements ServiceListener
{

    // the name of the ConfigurationAdmin service
    public static final String CONFIGURATION_ADMIN = "org.osgi.service.cm.ConfigurationAdmin";

    // the bundle context
    private BundleContext m_bundleContext;

    /**
     * The map of known components indexed by component name. The values are
     * either null (for name reservations) or implementations
     * of the {@link ComponentHolder} interface.
     * <p>
     * The {@link #checkComponentName(String)} will first add an entry to this
     * map with null value to reserve the name. After setting up
     * the component, the {@link #registerComponentHolder(String, ComponentHolder)}
     * method replaces the value of the named entry with the actual
     * {@link ComponentHolder}.
     *
     * @see #checkComponentName(String)
     * @see #registerComponentHolder(String, ComponentHolder)
     * @see #unregisterComponentHolder(String)
     */
    private final Map<ComponentRegistryKey, ComponentHolder<?>> m_componentHoldersByName;

    /**
     * The map of known components indexed by component configuration pid. The values are
     * Sets of the {@link ComponentHolder} interface. Normally, the configuration pid
     * is the component name, but since DS 1.2 (OSGi 4.3), a component may specify a specific
     * pid, and it is possible that different components refer to the same pid. That's why
     * the values of this map are Sets of ComponentHolders, allowing to lookup all components
     * which are using a given configuration pid.
     * This map is used when the ConfigurationSupport detects that a CM pid is updated. When
     * a PID is updated, the ConfigurationSupport listener class invokes the
     * {@link #getComponentHoldersByPid(String)} method which returns an iterator over all
     * components that are using the given pid for configuration.
     * <p>
     *
     * @see #registerComponentHolder(String, ComponentHolder)
     * @see #unregisterComponentHolder(String)
     * @see ConfigurationSupport#configurationEvent(org.osgi.service.cm.ConfigurationEvent)
     */
    private final Map<String, Set<ComponentHolder<?>>> m_componentHoldersByPid;

    /**
     * Map of components by component ID. This map indexed by the component
     * ID number (<code>java.lang.Long</code>) contains the actual
     * {@link AbstractComponentManager} instances existing in the system.
     *
     * @see #registerComponentId(AbstractComponentManager)
     * @see #unregisterComponentId(long)
     */
    private final Map<Long, AbstractComponentManager<?>> m_componentsById;

    /**
     * Counter to setup the component IDs as issued by the
     * {@link #registerComponentId(AbstractComponentManager)} method. This
     * counter is only incremented.
     */
    private long m_componentCounter = -1;

    // ConfigurationAdmin support -- created on demand upon availability of
    // the ConfigurationAdmin service
    private ConfigurationSupport configurationSupport;

    private final Map<ServiceReference<?>, List<Entry<?, ?>>> m_missingDependencies = new HashMap<ServiceReference<?>, List<Entry<?, ?>>>( );

    protected ComponentRegistry( BundleContext context )
    {
        m_bundleContext = context;
        m_componentHoldersByName = new HashMap<ComponentRegistryKey, ComponentHolder<?>>();
        m_componentHoldersByPid = new HashMap<String, Set<ComponentHolder<?>>>();
        m_componentsById = new HashMap<Long, AbstractComponentManager<?>>();

        // keep me informed on ConfigurationAdmin state changes
        try
        {
            context.addServiceListener(this, "(objectclass=" + CONFIGURATION_ADMIN + ")");
        }
        catch (InvalidSyntaxException ise)
        {
            // not expected (filter is tested valid)
        }

        // If the Configuration Admin Service is already registered, setup
        // configuration support immediately
        if (context.getServiceReference(CONFIGURATION_ADMIN) != null)
        {
            getOrCreateConfigurationSupport();
        }

    }


    public void dispose()
    {
        m_bundleContext.removeServiceListener(this);

        if (configurationSupport != null)
        {
            configurationSupport.dispose();
            configurationSupport = null;
        }

    }


    //---------- ComponentManager registration by component Id

    /**
     * Assigns a unique ID to the component, internally registers the
     * component under that ID and returns the assigned component ID.
     *
     * @param componentManager The {@link AbstractComponentManager} for which
     *      to assign a component ID and which is to be internally registered
     *
     * @return the assigned component ID
     */
    final long registerComponentId( final AbstractComponentManager<?> componentManager )
    {
        long componentId;
        synchronized ( m_componentsById )
        {
            componentId = ++m_componentCounter;

            m_componentsById.put( componentId, componentManager );
        }

        return componentId;
    }


    /**
     * Unregisters the component with the given component ID from the internal
     * registry. After unregistration, the component ID should be considered
     * invalid.
     *
     * @param componentId The ID of the component to be removed from the
     *      internal component registry.
     */
    final void unregisterComponentId( final long componentId )
    {
        synchronized ( m_componentsById )
        {
            m_componentsById.remove( componentId );
        }
    }


    //---------- ComponentHolder registration by component name

    /**
     * Checks whether the component name is "globally" unique or not. If it is
     * unique, it is reserved until the actual component is registered with
     * {@link #registerComponentHolder(String, ComponentHolder)} or until
     * it is unreserved by calling {@link #unregisterComponentHolder(String)}.
     * If a component with the same name has already been reserved or registered
     * a ComponentException is thrown with a descriptive message.
     *
     * @param bundle the bundle registering the component
     * @param name the component name to check and reserve
     * @throws ComponentException if the name is already in use by another
     *      component.
     */
    final ComponentRegistryKey checkComponentName( final Bundle bundle, final String name )
    {
        // register the name if no registration for that name exists already
        final ComponentRegistryKey key = new ComponentRegistryKey( bundle, name );
        ComponentHolder<?> existingRegistration = null;
        boolean present;
        synchronized ( m_componentHoldersByName )
        {
            present = m_componentHoldersByName.containsKey( key );
            if ( !present )
            {
                m_componentHoldersByName.put( key, null );
            }
            else
            {
                existingRegistration = m_componentHoldersByName.get( key );
            }
        }

        // there was a registration already, throw an exception and use the
        // existing registration to provide more information if possible
        if ( present )
        {
            String message = "The component name '" + name + "' has already been registered";

            if ( existingRegistration != null )
            {
                Bundle cBundle = existingRegistration.getActivator().getBundleContext().getBundle();
                ComponentMetadata cMeta = existingRegistration.getComponentMetadata();

                StringBuffer buf = new StringBuffer( message );
                buf.append( " by Bundle " ).append( cBundle.getBundleId() );
                if ( cBundle.getSymbolicName() != null )
                {
                    buf.append( " (" ).append( cBundle.getSymbolicName() ).append( ")" );
                }
                buf.append( " as Component of Class " ).append( cMeta.getImplementationClassName() );
                message = buf.toString();
            }

            throw new ComponentException( message );
        }

        return key;
    }


    /**
     * Registers the given component under the given name. If the name has not
     * already been reserved calling {@link #checkComponentName(String)} this
     * method throws a {@link ComponentException}.
     *
     * @param name The name to register the component under
     * @param componentHolder The component to register
     *
     * @throws ComponentException if the name has not been reserved through
     *      {@link #checkComponentName(String)} yet.
     */
    final void registerComponentHolder( final ComponentRegistryKey key, ComponentHolder<?> componentHolder )
    {
        Activator.log(LogService.LOG_DEBUG, null,
                "Registering component with pid {0} for bundle {1}",
                new Object[] {componentHolder.getComponentMetadata().getConfigurationPid(), key.getBundleId()},
                null);
        synchronized ( m_componentHoldersByName )
        {
            // only register the component if there is a m_registration for it !
            if ( m_componentHoldersByName.get( key ) != null )
            {
                // this is not expected if all works ok
                throw new ComponentException( "The component name '{0}" + componentHolder.getComponentMetadata().getName()
                    + "' has already been registered." );
            }

            m_componentHoldersByName.put( key, componentHolder );
        }

        synchronized (m_componentHoldersByPid)
        {
            // See if the component declares a specific configuration pid (112.4.4 configuration-pid)
            List<String> configurationPids = componentHolder.getComponentMetadata().getConfigurationPid();

            for ( String configurationPid: configurationPids )
            {
                // Since several components may refer to the same configuration pid, we have to
                // store the component holder in a Set, in order to be able to lookup every
                // components from a given pid.
                Set<ComponentHolder<?>> set = m_componentHoldersByPid.get( configurationPid );
                if ( set == null )
                {
                    set = new HashSet<ComponentHolder<?>>();
                    m_componentHoldersByPid.put( configurationPid, set );
                }
                set.add( componentHolder );
            }
        }
       
        if (configurationSupport != null)
        {
            configurationSupport.configureComponentHolder(componentHolder);
        }

  }

    /**
     * Returns the component registered under the given name or <code>null</code>
     * if no component is registered yet.
     */
    public final ComponentHolder<?> getComponentHolder( final Bundle bundle, final String name )
    {
        synchronized ( m_componentHoldersByName )
        {
            return m_componentHoldersByName.get( new ComponentRegistryKey( bundle, name ) );
        }
    }

    /**
     * Returns the set of ComponentHolder instances whose configuration pids are matching
     * the given pid.
     * @param pid the pid candidate
     * @return the set of ComponentHolders matching the singleton pid supplied
     */
    public final Collection<ComponentHolder<?>> getComponentHoldersByPid(TargetedPID targetedPid)
    {
        String pid = targetedPid.getServicePid();
        Set<ComponentHolder<?>> componentHoldersUsingPid = new HashSet<ComponentHolder<?>>();
        synchronized (m_componentHoldersByPid)
        {
            Set<ComponentHolder<?>> set = m_componentHoldersByPid.get(pid);
            // only return the entry if non-null and not a reservation
            if (set != null)
            {
                for (ComponentHolder<?> holder: set)
                {
                    if (targetedPid.matchesTarget(holder))
                    {
                        componentHoldersUsingPid.add( holder );
                    }
                }
            }
        }
        return componentHoldersUsingPid;
    }

    /**
     * Returns an array of all values currently stored in the component holders
     * map. The entries in the array are either String types for component
     * name reservations or {@link ComponentHolder} instances for actual
     * holders of components.
     */
    public final List<ComponentHolder<?>> getComponentHolders()
    {
      List<ComponentHolder<?>> all = new ArrayList<ComponentHolder<?>>();
        synchronized ( m_componentHoldersByName )
        {
          all.addAll(m_componentHoldersByName.values());
        }
        return all;
    }

    public final List<ComponentHolder<?>> getComponentHolders(Bundle...bundles)
    {
      List<ComponentHolder<?>> all =getComponentHolders();
        List<ComponentHolder<?>> holders = new ArrayList<ComponentHolder<?>>();
        for ( ComponentHolder<?> holder: all)
        {
          BundleComponentActivator activator = holder.getActivator();
          if (activator != null)
          {
            Bundle holderBundle = activator.getBundleContext().getBundle();
            for (Bundle b: bundles)
            {
              if (b == holderBundle)
              {
                holders.add(holder);
              }
            }
          }
        }
        return holders;
    }


    /**
     * Removes the component registered under that name. If no component is
     * yet registered but the name is reserved, it is unreserved.
     * <p>
     * After calling this method, the name can be reused by other components.
     */
    final void unregisterComponentHolder( final Bundle bundle, final String name )
    {
        unregisterComponentHolder( new ComponentRegistryKey( bundle, name ) );
    }


    /**
     * Removes the component registered under that name. If no component is
     * yet registered but the name is reserved, it is unreserved.
     * <p>
     * After calling this method, the name can be reused by other components.
     */
    final void unregisterComponentHolder( final ComponentRegistryKey key )
    {
        ComponentHolder<?> component;
        synchronized ( m_componentHoldersByName )
        {
            component = m_componentHoldersByName.remove( key );
        }

        if (component != null) {
            Activator.log(LogService.LOG_DEBUG, null,
                    "Unregistering component with pid {0} for bundle {1}",
                    new Object[] {component.getComponentMetadata().getConfigurationPid(), key.getBundleId()}, null);
            synchronized (m_componentHoldersByPid)
            {
                List<String> configurationPids = component.getComponentMetadata().getConfigurationPid();
                for ( String configurationPid: configurationPids )
                {
                    Set<ComponentHolder<?>> componentsForPid = m_componentHoldersByPid.get( configurationPid );
                    if ( componentsForPid != null )
                    {
                        componentsForPid.remove( component );
                        if ( componentsForPid.size() == 0 )
                        {
                            m_componentHoldersByPid.remove( configurationPid );
                        }
                    }
                }
            }
        }
    }

    //---------- base configuration support

    /**
     * Factory method to issue {@link ComponentHolder} instances to manage
     * components described by the given component <code>metadata</code>.
     */
    public <S> ComponentHolder<S> createComponentHolder( BundleComponentActivator activator, ComponentMetadata metadata )
    {
        return new ConfigurableComponentHolder<S>(activator, metadata);
    }


    //---------- ServiceListener

    /**
     * Called if the Configuration Admin service changes state. This
     * implementation is mainly interested in the Configuration Admin service
     * being registered <i>after</i> the Declarative Services setup to be able
     * to forward existing configuration.
     *
     * @param event The service change event
     */
    public void serviceChanged(ServiceEvent event)
    {
        if (event.getType() == ServiceEvent.REGISTERED)
        {
            ConfigurationSupport configurationSupport = getOrCreateConfigurationSupport();

            final ServiceReference<ConfigurationAdmin> caRef = (ServiceReference<ConfigurationAdmin>) event.getServiceReference();
            final ConfigurationAdmin service = m_bundleContext.getService(caRef);
            if (service != null)
            {
                try
                {
                    configurationSupport.configureComponentHolders(caRef, service);
                }
                finally
                {
                    m_bundleContext.ungetService(caRef);
                }
            }
        }
        else if (event.getType() == ServiceEvent.UNREGISTERING)
        {
            disposeConfigurationSupport();
        }
    }

    //---------- Helper method

    /**
     * Returns <code>true</code> if the <code>bundle</code> is to be considered
     * active from the perspective of declarative services.
     * <p>
     * As of R4.1 a bundle may have lazy activation policy which means a bundle
     * remains in the STARTING state until a class is loaded from that bundle
     * (unless that class is declared to not cause the bundle to start). And
     * thus for DS 1.1 this means components are to be loaded for lazily started
     * bundles being in the STARTING state (after the LAZY_ACTIVATION event) has
     * been sent.  Hence DS must consider a bundle active when it is really
     * active and when it is a lazily activated bundle in the STARTING state.
     *
     * @param bundle The bundle check
     * @return <code>true</code> if <code>bundle</code> is not <code>null</code>
     *          and the bundle is either active or has lazy activation policy
     *          and is in the starting state.
     *
     * @see <a href="https://issues.apache.org/jira/browse/FELIX-1666">FELIX-1666</a>
     */
    static boolean isBundleActive( final Bundle bundle )
    {
        if ( bundle != null )
        {
            if ( bundle.getState() == Bundle.ACTIVE )
            {
                return true;
            }

            if ( bundle.getState() == Bundle.STARTING )
            {
                // according to the spec the activationPolicy header is only
                // set to request a bundle to be lazily activated. So in this
                // simple check we just verify the header is set to assume
                // the bundle is considered a lazily activated bundle
                return bundle.getHeaders().get( Constants.BUNDLE_ACTIVATIONPOLICY ) != null;
            }
        }

        // fall back: bundle is not considered active
        return false;
    }

    private ConfigurationSupport getOrCreateConfigurationSupport()
    {
        if (configurationSupport == null)
        {
            configurationSupport = new ConfigurationSupport(m_bundleContext, this);
        }
        return configurationSupport;
    }

    private void disposeConfigurationSupport()
    {
        if (configurationSupport != null)
        {
            this.configurationSupport.dispose();
            this.configurationSupport = null;
        }
    }

    public synchronized <T> void missingServicePresent( final ServiceReference<T> serviceReference, ComponentActorThread actor )
    {
        final List<Entry<?, ?>> dependencyManagers = m_missingDependencies.remove( serviceReference );
        if ( dependencyManagers != null )
        {
            actor.schedule( new Runnable()
            {

                public void run()
                {
                    for ( Entry<?, ?> entry : dependencyManagers )
                    {
                        ((DependencyManager<?, T>)entry.getDm()).invokeBindMethodLate( serviceReference, entry.getTrackingCount() );
                    }
                }

                @Override
                public String toString()
                {
                    return "Late binding task of reference " + serviceReference + " for dependencyManagers " + dependencyManagers;
                }

            } );
        }
    }

    public synchronized <S, T> void registerMissingDependency( DependencyManager<S, T> dependencyManager, ServiceReference<T> serviceReference, int trackingCount )
    {
        //check that the service reference is from scr
        if ( serviceReference.getProperty( ComponentConstants.COMPONENT_NAME ) == null || serviceReference.getProperty( ComponentConstants.COMPONENT_ID ) == null )
        {
            return;
        }
        List<Entry<?, ?>> dependencyManagers = m_missingDependencies.get( serviceReference );
        if ( dependencyManagers == null )
        {
            dependencyManagers = new ArrayList<Entry<?, ?>>();
            m_missingDependencies.put( serviceReference, dependencyManagers );
        }
        dependencyManagers.add( new Entry<S, T>( dependencyManager, trackingCount ) );
    }

    private static class Entry<S,T>
    {
        private final DependencyManager<S, T> dm;
        private final int trackingCount;

        private Entry( DependencyManager<S, T> dm, int trackingCount )
        {
            this.dm = dm;
            this.trackingCount = trackingCount;
        }

        public DependencyManager<S, T> getDm()
        {
            return dm;
        }

        public int getTrackingCount()
        {
            return trackingCount;
        }
    }

}
TOP

Related Classes of org.apache.felix.scr.impl.ComponentRegistry

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.