Package org.apache.felix.servicebinder

Source Code of org.apache.felix.servicebinder.InstanceManager

/*
* 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.servicebinder;

import java.util.Properties;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.HashMap;

import org.apache.felix.servicebinder.architecture.Dependency;
import org.apache.felix.servicebinder.architecture.DependencyChangeEvent;
import org.apache.felix.servicebinder.architecture.Instance;
import org.apache.felix.servicebinder.architecture.InstanceChangeEvent;
import org.apache.felix.servicebinder.impl.ArchitectureServiceImpl;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.Bundle;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

/**
* A InstanceManager is created for every component instance. *  * When the InstanceManager is instantiated, a collection of DependencyManagers is * created. Each dependency manager corresponds to a required service *  * A InstanceManager follows a sequence of clearly defined steps. *  * 1.- Creation : the binder instance is created, its state becomes CREATED. This step is further divided *                in the following substeps: *                  - The binder instance checks if all of the dependencies are valid, if this *                    is false, it returns. *                  - If the dependendencies are valid, its state becomes executing. The object from *                    the instance class is created (if this object receives a ServiceBinderContext as *                    a parameter in its constructor, the context is passed to it. *                  - The validate() method is called on the dependency managers, this will cause *                    calls on the binding methods to occur *                  - The binder instance adds itself to the list of binder instances in the activator *                  - The binder instance registers the services implemented by the instance object. *  * 2.- Disposal : *  * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/

public class InstanceManager implements InstanceReference, Instance
{
    // The values ranging from 0 to 3 are public and are defined in InstanceReference
    static final int INSTANCE_CREATING = 4;
    static final int INSTANCE_VALIDATING = 5;
    static final int INSTANCE_INVALIDATING = 6;
    static final int INSTANCE_DESTROYING = 7;

    static final String m_states[]={"CREATED","VALID","INVALID",
                                    "DESTROYED","CREATING","VALIDATING",
                                    "INVALIDATING","DESTROYING"};

    // The state of this instance manager
    private int m_state = INSTANCE_CREATING;

    /**
     *
     * @uml.property name="m_instanceMetadata"
     * @uml.associationEnd multiplicity="(0 1)"
     */
    // The metadata
    private InstanceMetadata m_instanceMetadata;


    // The object that implements the service and that is bound to other services
    private Object m_implementorObject;

    // The dependency managers that manage every dependency
    private List m_dependencyManagers;

    // The ServiceRegistration
    private ServiceRegistration m_serviceRegistration;

    /**
     *
     * @uml.property name="m_activator"
     * @uml.associationEnd multiplicity="(0 1)"
     */
    // A reference to the GenericActivator
    private GenericActivator m_activator;

    /**
     *
     * @uml.property name="m_sbcontext"
     * @uml.associationEnd multiplicity="(0 1)"
     */
    // The context that will be passed to the objects
    private ServiceBinderContextImpl m_sbcontext;

    /**
     *
     * @uml.property name="m_instanceListener"
     * @uml.associationEnd multiplicity="(0 1)"
     */
    // Listeners to validation events
    private InstanceReferenceListener m_instanceListener = null;

    // Properties that can be attached to te InstanceManager
    private Properties m_localProperties = new Properties();
   
    // Flag that indicates that activate was called
    private boolean m_activateCalled = false;

    /**
    * Constructor that creates a collection of dependency managers that will be in
    * charge of the different dependencies for a particular instance.
    *
    * @param   activator A reference to the generic activator
    * @param   descriptor an InstanceMetadata that contains information found in the descriptor file
    * @throws  java.lang.ClassNotFoundException if the instance class (declared in the descriptor file) is not found
    * @throws  java.lang.NoSuchMethodException if the bind or unbind methods are not found on the instance class
    * @throws  org.osgi.framework.InvalidSyntaxException if the filter declared in the requires entry has an invalid syntax
    **/
    InstanceManager(GenericActivator activator,InstanceMetadata descriptor)
        throws ClassNotFoundException, NoSuchMethodException, InvalidSyntaxException
    {
        m_activator = activator;

        m_instanceMetadata = descriptor;

        m_dependencyManagers = new ArrayList();

        if (m_instanceMetadata.getDependencies().size() != 0)
        {
            Iterator dependencyit = m_instanceMetadata.getDependencies().iterator();

            while(dependencyit.hasNext())
            {
                DependencyMetadata currentdependency = (DependencyMetadata)dependencyit.next();

                DependencyManager depmanager = new DependencyManager(currentdependency);

                m_dependencyManagers.add(depmanager);

                // Register the dependency managers as listeners to service events so that they begin
                // to manage the dependency autonomously

                m_activator.getBundleContext().addServiceListener(depmanager,depmanager.getDependencyMetadata().getFilter());
            }
        }

        m_sbcontext = new ServiceBinderContextImpl(this);

        // Add this instance manager to the Generic activator list
        m_activator.addInstanceManager(this);

        setState(INSTANCE_CREATED);
    }

    /**
    * Validate this Instance manager.
    *
    * CONCURRENCY NOTE: This method can be called either after an instance manager is created
    * or after the instance is validated again after by the instance manager itself
    */
    synchronized void validate()
    {
        if (m_state == INSTANCE_VALID)
        {
            return;
        }
        else if (m_state != INSTANCE_INVALID && m_state !=INSTANCE_CREATED)
        {
            GenericActivator.error("InstanceManager : create() called for a non INVALID or CREATED InstanceManager ("+m_states[m_state]+")");
            return;
        }

        setState(INSTANCE_VALIDATING);

        // Test if all dependency managers are valid

        Iterator it = m_dependencyManagers.iterator();

        while (it.hasNext())
        {
            // It is not possible to call the isValid method yet in the DependencyManager
            // since they have not been initialized yet, but we can't call initialize
            // since the object where bindings will be done has not been created.
            // This test is necessary, because we don't want to instantiate
            // the object if the dependency managers won't be valid.
            DependencyManager dm = (DependencyManager)it.next();
            if (dm.getRequiredServiceRefs() == null && dm.getDependencyMetadata().isOptional() == false)
            {
                setState(INSTANCE_INVALID);
                return;
            }
        }

        // everything ok to go...

        try
        {
            Class c = m_activator.getClass().getClassLoader().loadClass(m_instanceMetadata.getImplementorName());
            try
            {
                Constructor cons = c.getConstructor(new Class[] {ServiceBinderContext.class});
                m_implementorObject = cons.newInstance(new Object[] {m_sbcontext});
            }
            catch(NoSuchMethodException ex)
            {
                // Aparently he doesn't want a ServiceBinderContext...
            }

            // Create from no-param constructor
            if (m_implementorObject == null)
            {
                m_implementorObject = c.newInstance();
            }

            /* is it a factory?
            if (m_implementorObject instanceof GenericFactory)
            {
                ((GenericFactory) m_implementorObject).setActivator(m_activator, this);
            }
            */

            // Allow somebody to proxy the object through the proxyProvidedServiceObject method
            // in the activator
            Object proxy = m_activator.proxyProvidedServiceObject(m_implementorObject, this.getInstanceMetadata());
            if (proxy != null)
            {
                m_implementorObject = proxy;
            }
            else
            {
                GenericActivator.error("InstanceManager : Proxy method returned a null value");
            }
        }
        catch (Throwable t)
        {
            // failure at creation
            GenericActivator.error("InstanceManager : Error during instantiation : "+t);
            t.printStackTrace();
            _invalidate();
            return;
        }
       
        // initial bindings

        it = m_dependencyManagers.iterator();

        while (it.hasNext())
        {
            DependencyManager dm = (DependencyManager)it.next();
            if (dm.initialize() == false)
            {
                _invalidate();
                return;
            }
        }
       
        // We need to check if we are still validating because it is possible that when we
        // registered the service above our thread causes an instance to become valid which
        // then registered a service that then generated an event that we needed that
        // caused validate() to be called again, thus if we are not still VALIDATING, it
        // means we are already VALID.
        if (m_state == INSTANCE_VALIDATING)
        {
            // activate

            if (m_implementorObject instanceof Lifecycle)
            {
                try
                {
                    ((Lifecycle)m_implementorObject).activate();
                    this.m_activateCalled=true;
                }
                catch(Exception e)
                {
                    GenericActivator.error("InstanceManager : exception during activate:"+e);
                    e.printStackTrace();
                    _invalidate();
                    return;
                }
            }

            // validated!

            fireInstanceReferenceValidated();
            setState(INSTANCE_VALID);
        }
       

        // register services

        boolean reg = requestRegistration();

        if (!reg)
        {
            GenericActivator.error("InstanceManager : registration of the services failed...");
            _invalidate();
            return;
        }
       
        // Configuration ended successfuly.

    }

    /**
     * This method invalidates the InstanceManager
     *
     * CONCURRENCY NOTE: This method may be called either from application code or event thread.
    **/
    synchronized void invalidate()
    {
        if (m_state == INSTANCE_INVALID)
        {
            return;
        }
        else if (m_state != INSTANCE_VALID && m_state != INSTANCE_DESTROYING)
        {
            GenericActivator.error("InstanceManager : invalidate() called for a non VALID InstanceManager ("+m_states[m_state]+")");
            return;
        }

        if (m_state != INSTANCE_DESTROYING)
        {
            setState(INSTANCE_INVALIDATING);
        }

        // Fire invalidating events
        fireInstanceReferenceInvalidating();

        _invalidate();


    }

    /**
     * this method invalidates the InstanceManager without performing any of the callbacks
     * associated with the Lifecycle interface or the InstanceReference event listeners.
    **/
    private void _invalidate()
    {
        // Unregister services

        requestUnregistration();
       
        if(m_activateCalled==true)
        {       
            // Call deactivate on the Lifecycle

            if (m_implementorObject instanceof Lifecycle)
            {
                try
                {
                    ((Lifecycle)m_implementorObject).deactivate();
                }
                catch(Exception e)
                {
                    GenericActivator.error("InstanceManager : exception during call to deactivate():"+e);
                }
            }

        }

        // Unbind all services

        Iterator it = m_dependencyManagers.iterator();

        while (it.hasNext())
        {
            DependencyManager dm = (DependencyManager)it.next();
            dm.unbindAll();
        }

        //m_activator.removeInstanceManager(this);

        // remove all instances from a factory

        /*
        if (m_implementorObject instanceof GenericFactory)
        {
            ((GenericFactory)m_implementorObject).invalidateInstances();
        }
        */

        // Release the object reference

        m_implementorObject = null;

        GenericActivator.trace("InstanceManager from bundle ["
           + m_activator.getBundleContext().getBundle().getBundleId() + "] was invalidated.");

        if (m_state != INSTANCE_DESTROYING)
        {
            setState(INSTANCE_INVALID);
        }
    }

    /**
     * This method should be called to completely remove the InstanceManager from the system.
     * This means that the dependency managers will stop listening to events.
     *
     * CONCURRENCY NOTE: This method is only called from the GenericActivator, which is
     *                   essentially application code and not via events.
    **/
    synchronized void destroy()
    {
        if (m_state == INSTANCE_DESTROYED)
        {
            return;
        }

        // Theoretically this should never be in any state other than VALID or INVALID,
        // because validate is called right after creation.
        boolean invalidatefirst = (m_state == INSTANCE_VALID);

        setState(INSTANCE_DESTROYING);

        // Stop the dependency managers to listen to events...
        Iterator it = m_dependencyManagers.iterator();

        while (it.hasNext())
        {
            DependencyManager dm = (DependencyManager)it.next();
            m_activator.getBundleContext().removeServiceListener(dm);
        }

        if (invalidatefirst)
        {
            invalidate();
        }

        m_dependencyManagers.clear();

        m_instanceListener = null;

        GenericActivator.trace("InstanceManager from bundle ["
           + m_activator.getBundleContext().getBundle().getBundleId() + "] was destroyed.");

        m_activator.removeInstanceManager(this);
        setState(INSTANCE_DESTROYED);

        m_activator = null;
    }

    /**
    * Returns the InstanceMetadata
    */
    public InstanceMetadata getInstanceMetadata()
    {
        return m_instanceMetadata;
    }

    /**
    * Get the object that is implementing this descriptor
    *
    * @return the object that implements
    */
    public Object getObject()
    {
        return m_implementorObject;
    }

    /**
    * Request the registration of the service provided by this binder instance
    *
    * @return returns false if registration was not successful,
    *                returns true if registration was successful
    **/
    boolean requestRegistration()
    {
        if (!m_instanceMetadata.instanceRegistersServices())
        {
            return true;
        }
        else if (m_implementorObject == null)
        {
            GenericActivator.error("GenericActivator : Cannot register, implementor object not created!");
            return false;
        }
        else if (m_serviceRegistration != null)
        {
            GenericActivator.error("GenericActivator : Cannot register, binder instance already registered :"
                + m_instanceMetadata.getImplementorName());
            return true;
        }

        // Check validity of dependencies before registering !
        Iterator it = m_dependencyManagers.iterator();
        while (it.hasNext())
        {
            DependencyManager dm = (DependencyManager)it.next();
            if (dm.isValid() == false)
                return false;
        }

        // When registering a factory, add an instanceClass property which is an array
        // of service interfaces implemented by the objects created by the factory.

        if (m_instanceMetadata.isFactory())
        {
            if(m_instanceMetadata.getProperties().get("instanceClass") == null)
            {
                m_instanceMetadata.getProperties().put("instanceClass",m_instanceMetadata.getInstantiates().getInterfaces());
            }
        }

        m_serviceRegistration = m_activator.getBundleContext().registerService(
            m_instanceMetadata.getInterfaces(), m_implementorObject, m_instanceMetadata.getProperties());

        GenericActivator.trace("Generic Activator : InstanceManager inside bundle ["
            + m_activator.getBundleContext().getBundle().getBundleId()
            + "] successfully registered its services !");

        return true;
    }


    /**
    *
    * Request the unfegistration of the service provided by this binder instance
    *
    **/
    void requestUnregistration()
    {
        if (m_serviceRegistration != null)
        {
            m_serviceRegistration.unregister();
            m_serviceRegistration = null;

            GenericActivator.trace("Generic Activator : InstanceManager inside bundle ["
                + m_activator.getBundleContext().getBundle().getBundleId()
                + "] unregistered its services !");

         }
    }

    /**
    * Get the state
    */
    public int getState()
    {
        return m_state;
    }

    /**
    * Get the state
    */
    public long getBundleId()
    {
        return m_activator.getBundleContext().getBundle().getBundleId();
    }

    /**
     * Get a property associated with this instance. For classes
     * implementing this method, special care must be taken for
     * values implementing <tt>InstanceReference.ValueHolder</tt>.
     * In such cases, the value itself should not be returned, but
     * the value of <tt>InstanceReference.ValueHolder.get()</tt>
     * should be returned instead. This may be used to defer
     * creating value objects in cases where creating the value
     * object is expensive.
     * @param name the name of the property to retrieve.
     * @return the value of the associated property or <tt>null</tt>.
    **/
    public Object get(String name)
    {
        GenericActivator.trace("InstanceManager.get("+name+")");

        if(name.equals(InstanceReference.INSTANCE_STATE))
        {
            return new Integer(m_state);
        }
        else if(name.equals(InstanceReference.INSTANCE_METADATA))
        {
            return getInstanceMetadata();
        }
        else if(name.equals(InstanceReference.INSTANCE_BUNDLE))
        {
            return new Integer((int) getBundleId());
        }
        else if(name.equals(InstanceReference.INSTANCE_DEPENDENCIES))
        {
            return getDependencies();
        }
        else
        {
            Object ret = m_localProperties.get(name);

            if (ret != null)
            {
                if (ret instanceof ValueHolder)
                {
                    return ((ValueHolder)ret).get(this);
                }
                return ret;
            }

            return m_instanceMetadata.getProperties().get(name);
        }

    }

    /**
     * Associate a property with this instance. For classes
     * implementing this method, special care must be taken for
     * values implementing <tt>InstanceReference.ValueHolder</tt>.
     * In such cases, the value itself should not be returned, but
     * the value of <tt>InstanceReference.ValueHolder.get()</tt>
     * should be returned instead. This may be used to defer
     * creating value objects in cases where creating the value
     * object is expensive.
     * @param name the name of the property to add.
     * @param obj the value of the property.
    **/
    public void put(String name, Object obj)
    {
        m_localProperties.put(name,obj);
    }

    /**
     * Adds an instance reference listener to listen for changes to
     * the availability of the underlying object associated with this
     * instance reference.
     * @param l the listener to add.
    **/
    public void addInstanceReferenceListener(InstanceReferenceListener l)
    {
        m_instanceListener = StateChangeMulticaster.add(m_instanceListener, l);
    }

    /**
     * Removes an instance reference listener.
     * @param l the listener to remove.
    **/
    public void removeInstanceReferenceListener(InstanceReferenceListener l)
    {
        m_instanceListener = StateChangeMulticaster.remove(m_instanceListener, l);
    }

    /**
     * Fires an event when the instance reference has been validated
    **/
    protected void fireInstanceReferenceValidated()
    {
        try
        {
            if (m_instanceListener != null)
            {
                m_instanceListener.validated(new InstanceReferenceEvent(this));
            }
        }
        catch(Exception ex)
        {
            // Ignore any exception
        }
    }

    /**
     * Fires an event when the instance reference is invalidating
    **/
    protected void fireInstanceReferenceInvalidating()
    {
        try
        {
            if (m_instanceListener != null)
            {
                m_instanceListener.invalidating(new InstanceReferenceEvent(this));
            }
        }
        catch(Exception ex)
        {
            // Ignore any exception
        }
    }

    /**
     * sets the state of the instanceManager
    **/
    synchronized void setState(int newState)
    {
        m_state = newState;

        if(m_state == INSTANCE_CREATED || m_state == INSTANCE_VALID || m_state == INSTANCE_INVALID || m_state == INSTANCE_DESTROYED)
        {
            m_activator.fireInstanceChangeEvent(new InstanceChangeEvent(this,m_instanceMetadata,m_state));
        }
    }

    /**
     * Get an array of dependencies for this instance. This method is declared
     * in the Instance interface
     *
     * @return an array of Dependencies
    **/
    public Dependency [] getDependencies()
    {
        Dependency deps [] = new Dependency[m_dependencyManagers.size()];
        return (Dependency[]) m_dependencyManagers.toArray(deps);
    }

    /**
     * Get a list of child instances in case this is a factory
     *
     * @return an array of Instances
    **/
     public Instance[] getChildInstances()
    {
        /*
        if(m_implementorObject != null && m_implementorObject instanceof GenericFactory)
        {
            List instanceRefs = ((GenericFactory)m_implementorObject).getInstanceReferences();
            Instance [] instances = new Instance[instanceRefs.size()];
            instances = (Instance [])instanceRefs.toArray(instances);
            return instances;
        }
        */
        return null;
    }

/**
* This class implements the ServiceBinderContext, which cannot be directly * implemented by the activator because of the getInstanceReference() method
*/

    class ServiceBinderContextImpl implements ServiceBinderContext
    {

        /**
         *
         * @uml.property name="m_parent"
         * @uml.associationEnd multiplicity="(0 1)"
         */
        private InstanceReference m_parent;

        ServiceBinderContextImpl(InstanceReference parent)
        {
            m_parent = parent;
        }

        /**
         * Get the bundle context
        **/
        public BundleContext getBundleContext()
        {
            return m_activator.getBundleContext();
        }

        /**
         * Return all of the InstanceReferences created in the same bundle
        **/
        public List getInstanceReferences()
        {
            return m_activator.getInstanceReferences();
        }

        /**
         * Get the parent InstanceReference
        **/
        public InstanceReference getInstanceReference()
        {
            return m_parent;
        }
    }

/**
* The DependencyManager task is to listen to service events and to call the * bind/unbind methods on a given object. It is also responsible for requesting * the unregistration of a service in case a dependency is broken.
*/

    class DependencyManager implements ServiceListener, Dependency
    {

        /**
         *
         * @uml.property name="m_dependencyMetadata"
         * @uml.associationEnd multiplicity="(0 1)"
         */
        private DependencyMetadata m_dependencyMetadata;

        private Map m_boundServices = new HashMap();
        private Method m_bindMethod;
        private Method m_unbindMethod;
        private boolean m_isValid;
        private int m_depState;
        private boolean m_receivesRef = false;

        /**
         * Constructor that receives several parameters.
         *
         * @param   dependency  An object that contains data about the dependency
        **/
        DependencyManager(DependencyMetadata dependency) throws ClassNotFoundException, NoSuchMethodException
        {
            m_dependencyMetadata = dependency;
            m_isValid = false;

            m_bindMethod = getTargetMethod(m_dependencyMetadata.getBindMethodName(),InstanceManager.this.getInstanceMetadata().getImplementorName(),m_dependencyMetadata.getServiceName());
            m_unbindMethod = getTargetMethod(m_dependencyMetadata.getUnbindMethodName(),InstanceManager.this.getInstanceMetadata().getImplementorName(),m_dependencyMetadata.getServiceName());

            setStateDependency(DependencyChangeEvent.DEPENDENCY_CREATED);
        }

        /**
         * initializes a dependency. This method binds all of the service occurrences to the instance object
         *
         * @return true if the operation was successful, false otherwise
        **/
        boolean initialize()
        {
            if(getObject() == null)
            {
                return false;
            }

            ServiceReference refs[] = getRequiredServiceRefs();

            if (refs == null && m_dependencyMetadata.isOptional() == false)
            {
                m_isValid = false;
                setStateDependency(DependencyChangeEvent.DEPENDENCY_INVALID);
                return m_isValid;
            }

            m_isValid = true;
            setStateDependency(DependencyChangeEvent.DEPENDENCY_VALID);

            if (refs != null)
            {
                int max = 1;
                boolean retval = true;

                if (m_dependencyMetadata.isMultiple() == true)
                {
                    max = refs.length;
                }

                for (int index = 0; index < max; index++)
                {
                    retval = callBindMethod(refs[index]);
                    if(retval == false && (max == 1))
                    {
                        // There was an exception when calling the bind method
                        GenericActivator.error("Dependency Manager: Possible exception in the bind method during initialize()");
                        m_isValid = false;
                        setStateDependency(DependencyChangeEvent.DEPENDENCY_INVALID);
                        return m_isValid;
                    }
                }
            }

            return m_isValid;
        }

        /**
         * Called upon a service event. This method is responsible for calling the
         * binding and unbinding methods and also to request the eventual unregistering
         * of a service when a dependency breaks
         *
         * @param evt  The ServiceEvent
        **/
        public void serviceChanged(ServiceEvent evt)
        {
            synchronized (InstanceManager.this)
            {
                // If the object is being created or destroyed, we can safely ignore events.
                if (m_state == INSTANCE_DESTROYING || m_state == INSTANCE_DESTROYED || m_state == INSTANCE_CREATING || m_state == INSTANCE_CREATED)
                {
                    return;
                }

                // If we are in the process of invalidating, it is not necessary to pass
                // unregistration events, since we are unbinding everything anyway.
                else if (m_state == INSTANCE_INVALIDATING && evt.getType() == ServiceEvent.UNREGISTERING)
                {
                    return;
                }

                // We do not have an entry for VALIDATING because it is reentrant.

                // A service is unregistering
                if (evt.getType() == ServiceEvent.UNREGISTERING)
                {
                    if (m_boundServices.keySet().contains(evt.getServiceReference()) == true)
                    {
                        // A static dependency is broken the instance manager will be invalidated
                        if (m_dependencyMetadata.isStatic())
                        {
                            m_isValid = false;
                            setStateDependency(DependencyChangeEvent.DEPENDENCY_INVALID);
                            try
                            {
                                GenericActivator.trace("Dependency Manager: Static dependency is broken");
                                invalidate();
                                GenericActivator.trace("Dependency Manager: RECREATING");
                                validate();
                            }
                            catch(Exception e)
                            {
                                e.printStackTrace();
                            }
                        }
                        // dynamic dependency
                        else
                        {
                            // Release references to the service, call unbinder method
                            // and eventually request service unregistration

                            callUnbindMethod(evt.getServiceReference());

                            // The only thing we need to do here is check if we can reinitialize
                            // once the bound services becomes zero. This tries to repair dynamic
                            // 1..1 or rebind 0..1, since replacement services may be available.
                            // In the case of aggregates, this will only invalidate them since they
                            // can't be repaired.
                            if (m_boundServices.size() == 0)
                            {
                                // try to reinitialize
                                if (!initialize())
                                {
                                    if (!m_dependencyMetadata.isOptional())
                                    {
                                        GenericActivator.trace("Dependency Manager: Mandatory dependency not fullfilled and no replacements available... unregistering service...");
                                        invalidate();
                                        GenericActivator.trace("Dependency Manager: RECREATING");
                                        validate();
                                    }
                                }
                            }
                        }
                    }
                }
                // A service is registering.
                else if (evt.getType() == ServiceEvent.REGISTERED)
                {
                    if (m_boundServices.keySet().contains(evt.getServiceReference()) == true)
                    {
                        // This is a duplicate
                        GenericActivator.trace("DependencyManager : ignoring REGISTERED ServiceEvent (already bound)");
                    }
                    else
                    {
                        m_isValid = true;
                        setStateDependency(DependencyChangeEvent.DEPENDENCY_VALID);

                        // If the InstanceManager is invalid, a call to validate is made
                        // which will fix everything.
                        if (InstanceManager.this.getState() != INSTANCE_VALID)
                        {
                            validate();
                        }
                        // Otherwise, this checks for dynamic 0..1, 0..N, and 1..N it never
                        // checks for 1..1 dynamic which is done above by the validate()
                        else if (!m_dependencyMetadata.isStatic())
                        {
                            // For dependency that are aggregates, always bind the service
                            // Otherwise only bind if bind services is zero, which captures the 0..1 case
                            if (m_dependencyMetadata.isMultiple() || m_boundServices.size() == 0)
                            {
                                callBindMethod(evt.getServiceReference());
                            }
                        }
                    }
                }
            }
        }

        /**
         * Revoke all bindings. This method cannot throw an exception since it must try
         * to complete all that it can
         *
        **/
        void unbindAll()
        {
            Object []allrefs = m_boundServices.keySet().toArray();

            if (allrefs == null)
                return;

            for (int i = 0; i < allrefs.length; i++)
            {
                callUnbindMethod((ServiceReference)allrefs[i]);
            }
        }

        /**
         * Test if this dependency managed by this object is valid
        **/
        boolean isValid()
        {
            return m_isValid;
        }

        /**
         *
         * Returns an array containing the service references that are pertinent to the
         * dependency managed by this object. This method filters out services that
         * belong to bundles that are being (or are actually) shutdown. This is an issue
         * since is not clearly specified in the OSGi specification if a getServiceReference
         * call should return the services that belong to bundles that are stopping.
         *
         * @return an array of ServiceReferences valid in the context of this dependency
        **/
        ServiceReference [] getRequiredServiceRefs()
        {
            try
            {
                ArrayList list=new ArrayList();
                ServiceReference temprefs[] =
                    m_activator.getBundleContext().getServiceReferences(m_dependencyMetadata.getServiceName(), m_dependencyMetadata.getFilter());

                if (temprefs == null)
                {
                    return null;
                }

                for (int i = 0; i < temprefs.length; i++)
                {
                     if (temprefs[i].getBundle().getState() == Bundle.ACTIVE
                            || temprefs[i].getBundle().getState() == Bundle.STARTING)
                     {
                         list.add(temprefs[i]);
                     }
                }

                return (ServiceReference []) list.toArray(new ServiceReference [temprefs.length]);

            }
            catch (Exception e)
            {
                GenericActivator.error("DependencyManager: exception while getting references :"+e);
                return null;
            }
        }

        /**
         * Gets a target method based on a set of parameters
         *
         * @param methodname The name of the method
         * @param targetClass the class to which the method belongs to
         * @param paramClass the class of the parameter that is passed to the method
         * @throws java.lang.ClassNotFoundException if the class was not found
         * @throws java.lang.NoSuchMethodException if the method was not found
        **/
        private Method getTargetMethod(String methodname, String targetClass, String paramClass)
            throws ClassNotFoundException, NoSuchMethodException
        {
            Class targetclass = m_activator.getClass().getClassLoader().loadClass(targetClass);

            Method method = null;
           
            try
            {
                method = targetclass.getMethod(methodname,
                    new Class[]{m_activator.getClass().getClassLoader().loadClass(paramClass)});
              
            }
            catch(NoSuchMethodException ex)
            {
                // Test if the bind method receives a ServiceReference as the first parameter
               
                method = targetclass.getMethod(methodname,
                    new Class[]{ServiceReference.class, m_activator.getClass().getClassLoader().loadClass(paramClass)});
              
                m_receivesRef = true;
            }
           
            return method;
        }

        /**
         * Call the bind method. In case there is an exception while calling the bind method, the service
         * is not considered to be bound to the instance object
         *
         * @param ref A ServiceReference with the service that will be bound to the instance object
         * @return true if the call was successful, false otherwise
        **/
        boolean callBindMethod(ServiceReference ref)
        {
            try
            {
                Object requiredService = m_activator.getBundleContext().getService(ref);
                Object proxy = m_activator.proxyRequiredServiceObject(requiredService,m_dependencyMetadata);
               
                if(proxy == null)
                {
                    // ignore a null return value from the proxy method
                    proxy = requiredService;
                }
                if(m_receivesRef == false)
                {
                    m_bindMethod.invoke(getObject(),new Object[] {proxy});
                }
                else
                {
                    m_bindMethod.invoke(getObject(),new Object[] {ref,proxy});
                }
                m_boundServices.put(ref,proxy);
               
                return true;
            }
            catch(Exception e)
            {
                if(e instanceof InvocationTargetException)
                {
                    InvocationTargetException ite = (InvocationTargetException) e;
                    GenericActivator.error("DependencyManager : exception while invoking "+m_dependencyMetadata.getBindMethodName()+" :"+ite.getTargetException());
                }
                else
                {
                    GenericActivator.error("DependencyManager : exception while invoking "+m_dependencyMetadata.getBindMethodName()+" :"+e);
                }
                e.printStackTrace();
                return false;
            }
        }

        /**
         * Call the unbind method
         *
         * @param ref A service reference corresponding to the service that will be unbound
        **/
        void callUnbindMethod(ServiceReference ref)
        {
            Object requiredService = m_boundServices.get(ref);
            if (requiredService == null)
            {
                GenericActivator.error("DependencyManager : callUnbindMethod UNBINDING UNKNOWN SERVICE !!!!");
                return;
            }

            try
            {
                if(m_receivesRef == false)
                {
                    m_unbindMethod.invoke(getObject(),new Object [] {requiredService});
                }
                else
                {
                    m_unbindMethod.invoke(getObject(),new Object [] {ref, requiredService});
                }
               
                m_boundServices.remove(ref);
                m_activator.getBundleContext().ungetService(ref);
            }
            catch(Exception e)
            {
                if(e instanceof InvocationTargetException)
                {
                    InvocationTargetException ite = (InvocationTargetException) e;
                    GenericActivator.error("DependencyManager : exception while invoking "+m_dependencyMetadata.getUnbindMethodName()+" :"+ite.getTargetException());
                }
                else
                {
                    GenericActivator.error("DependencyManager : exception while invoking "+m_dependencyMetadata.getUnbindMethodName()+" :"+e);
                }
                e.printStackTrace();
            }
        }

        /**
         * Return the dependency descriptor
         *
         * @return the corresponding dependency descriptor
        **/
        public DependencyMetadata getDependencyMetadata()
        {
            return m_dependencyMetadata;
        }

        /**
         * Fire a state change event.
         *
         * @param state the state of the dependency manager
        **/
        void setStateDependency(int state)
        {
            m_depState = state;
            m_activator.fireDependencyChangeEvent(new DependencyChangeEvent(this,m_dependencyMetadata,state));
        }

        /**
         * Get the state of the dependency.
         *
         * @return the state of the dependency manager
        **/
        public int getDependencyState()
        {
            return m_depState;
        }

        /**
         * Get the bound service objects. This method is declared in the Dependency interface
         * and is used for to get a model.
         *
         * @return an array containing the bound service objects
        **/
        public Instance[] getBoundInstances()
        {
            Object bound[] = m_boundServices.values().toArray();

            ArrayList tempArray = new ArrayList();

            for(int i=0; i<bound.length; i++)
            {
                InstanceReference ref = ArchitectureServiceImpl.findInstanceReference(bound[i]);
                if(ref != null)
                {
                    tempArray.add(ref);
                }
            }

            Instance instances[]= new Instance[tempArray.size()];
            instances = (Instance [])tempArray.toArray(instances);

            return instances;


        }
    }

/**
* @version X.XX Feb 3, 2004  * @author Humberto Cervantes
*/

    static public class StateChangeMulticaster implements InstanceReferenceListener
    {

        /**
         *
         * @uml.property name="a"
         * @uml.associationEnd multiplicity="(0 1)"
         */
        protected InstanceReferenceListener a;

        /**
         *
         * @uml.property name="b"
         * @uml.associationEnd multiplicity="(0 1)"
         */
        protected InstanceReferenceListener b;

        protected StateChangeMulticaster(InstanceReferenceListener a, InstanceReferenceListener b)   
        {       
            this.a = a;       
            this.b = b;   
        }   
   
   
        public void validated(InstanceReferenceEvent e)   
        {
            a.validated(e);       
            b.validated(e);
        }
   
        public void invalidating(InstanceReferenceEvent e)   
        {
            a.invalidating(e);       
            b.invalidating(e);
        }
   
        public static InstanceReferenceListener add(InstanceReferenceListener a, InstanceReferenceListener b)
        {
            if (a == null)
                return b;
            else if (b == null)
                return a;
            else
                return new StateChangeMulticaster(a, b);
        }
   
        public static InstanceReferenceListener remove(InstanceReferenceListener a, InstanceReferenceListener b)
        {
            if ((a == null) || (a == b))           
                return null;       
            else if (a instanceof StateChangeMulticaster)           
                return add (remove (((StateChangeMulticaster) a).a, b),remove (((StateChangeMulticaster) a).b, b));       
            else           
                return a;   
        }
    }
}
TOP

Related Classes of org.apache.felix.servicebinder.InstanceManager

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.