Package org.apache.beehive.controls.runtime.bean

Source Code of org.apache.beehive.controls.runtime.bean.ControlBeanContext

package org.apache.beehive.controls.runtime.bean;
/*
* Copyright 2004 The Apache Software Foundation.
*
* Licensed 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.
*
* $Header:$
*/

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyVetoException;
import java.beans.beancontext.BeanContext;
import java.beans.beancontext.BeanContextServiceRevokedEvent;
import java.beans.beancontext.BeanContextServiceRevokedListener;
import java.beans.beancontext.BeanContextServiceProvider;
import java.beans.beancontext.BeanContextServices;
import java.beans.beancontext.BeanContextServicesSupport;
import java.util.*;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;

import org.apache.beehive.controls.api.ControlException;
import org.apache.beehive.controls.api.bean.ControlInterface;
import org.apache.beehive.controls.api.context.ControlHandle;
import org.apache.beehive.controls.api.properties.AnnotatedElementMap;
import org.apache.beehive.controls.api.properties.BeanPropertyMap;
import org.apache.beehive.controls.api.properties.PropertyMap;
import org.apache.beehive.controls.api.properties.PropertySet;
import org.apache.beehive.controls.api.properties.PropertySetProxy;

/**
* The ControlBeanContext implements the basic BeanContextServices implementation
* for ControlBeans.
*
* It provides several basic functions:
*  - it defines the generic services that are available for all control containers
*  - it acts as the base class for other container service implementations
*  - it acts as the BeanContextServicesRevokedListener when an associated control
*    bean has lost access to services.
*/
public class ControlBeanContext
             extends BeanContextServicesSupport
             implements BeanContextServiceRevokedListener,
                        org.apache.beehive.controls.api.context.ControlBeanContext,
                        java.beans.PropertyChangeListener,
                        java.beans.VetoableChangeListener
{
    /**
     * Creates a new ControlBeanContext instance associated with a specific
     * control bean.
     *
     * @param bean The control bean that contains/scopes the ControlBeanContext.  If null,
                   it means the ControlBeanContext is for a top-level container.
     */
    protected ControlBeanContext(ControlBean bean)
    {
        _bean = bean;
    }

    /**
     * The ControlBeanContextProvider inner class acts as a single BeanContext service
     * provider for the ControlBeanContext service class.  The implementation is simple,
     * because the runtime ControlBeanContext implementation class directly implements
     * this interface.
     */
    private static class ControlBeanContextProvider implements BeanContextServiceProvider
    {
        //
        // BeanContextServiceProvider.getService()
        //
        public Object getService(BeanContextServices bcs, Object requestor, Class serviceClass,
                                 Object serviceSelector)
        {
            //
            // Contextual services for a ControlBean is provided by the peer context
            // instance.
            //
            if (requestor instanceof ControlBean)
                return ((ControlBean)requestor).getControlBeanContext();

            return null;
        }

        //
        // BeanContextServiceProvider.releaseService()
        //
        public void releaseService(BeanContextServices bcs, Object requestor, Object service)
        {
            // noop, because context exists whether referenced or not
        }

        //
        // BeanContextServiceProvider.getContextServiceSelectors()
        //
        public Iterator getCurrentServiceSelectors(BeanContextServices bcs, Class serviceClass)
        {
            return null;    // no selectors
        }
    }

    /**
     * A singleton instance of the ControlBeanContextProvider class is that will be registered
     * on all ControlBeanContext instances.  The provider can be a singleton because it is
     * completely stateless and thread-safe.
     */
    static private ControlBeanContextProvider theProvider = new ControlBeanContextProvider();

    /**
     * Called by BeanContextSupport superclass during construction and deserialization to
     * initialize subclass transient state
     */
    public void initialize()
    {
        super.initialize();

        //
        // Register the ControlBeanContext provider on all new context instances.
        //
        addService(org.apache.beehive.controls.api.context.ControlBeanContext.class, theProvider);
    }

    /**
     * Implements the BeanContextServicesRevokedListener.servicesRevoked method.  This is
     * called if the associated ControlBean has requested a service that is being subsequently
     * revoked.
     */
    public void serviceRevoked(BeanContextServiceRevokedEvent bcsre)
    {
        //
        // This can happen, if the control is disassociated from a parent context that is
        // providing services.
        //
    }

    /**
     * Overrides the BeanContextChild.setBeanContext() method.  This hook is used to perform
     * additional processing that needs to occur when the control is associated with a new
     * nesting context.
     */
    public synchronized void setBeanContext(BeanContext beanContext) throws PropertyVetoException
    {
        ControlBeanContext cbcs = null;

        if (beanContext != null)
        {
            //
            // ControlBeans can only be nested in context service instances that derive
            // from ControlBeanContext.
            //
            if (!(beanContext instanceof ControlBeanContext))
            {
                PropertyChangeEvent pce = new PropertyChangeEvent(_bean, "beanContext",
                                                                  getBeanContext(), beanContext);
                throw new PropertyVetoException("Context does not support nesting controls: " +
                                                beanContext.getClass(), pce);
            }

            cbcs = (ControlBeanContext)beanContext;
        }


        super.setBeanContext(beanContext);

        resetControlID();

        _hasSingleThreadedParent = cbcs != null ? cbcs.isSingleThreadedContainer() : false;

        //
        // Notify the bean that its context (container) has been set.
        //
        if (_bean != null)
            _bean.setBeanContext(beanContext);
    }

    /**
     * The NameGenerator class is a simple helper class that creates new unique names based
     * upon a base prefix and an incrementing counter
     */
    private static class NameGenerator implements java.io.Serializable  
    {
        NameGenerator(String namePrefix)
        {
            _namePrefix = namePrefix;
        }

        /**
         * Get the next unique name
         */
        public synchronized String next()
        {
            return _namePrefix + _nextIndex++;
        }

        int _nextIndex = 0;
        String _namePrefix;
    }

    /**
     * Returns a new NameGenerator instance based upon a particular naming
     * prefix.
     */
    private NameGenerator getNameGenerator(String namePrefix)
    {
        synchronized(this)
        {
            if (_nameGenerators == null)
                _nameGenerators = new HashMap<String,NameGenerator>();

            NameGenerator nameGenerator = (NameGenerator)_nameGenerators.get(namePrefix);
            if (nameGenerator == null)
            {
                nameGenerator = new NameGenerator(namePrefix);
                _nameGenerators.put(namePrefix, nameGenerator);
            }
            return nameGenerator;
        }
    }

    /**
     * Generates a new unique control ID for an instance of the target class
     */
    public String generateUniqueID(Class clazz)
    {
        String namePrefix = clazz.getName();
        int dotIndex = namePrefix.lastIndexOf('.');
        if (dotIndex > 0)
            namePrefix = namePrefix.substring(dotIndex+1);
        NameGenerator nameGenerator = getNameGenerator(namePrefix);
        return nameGenerator.next();
    }

    /**
     * Overrides the BeanContextSupport.add() method to perform additional validation
     * that is unique for ControlBeans containers.
     */
    public boolean add(Object targetChild)
    {
        //
        // The context can contain ControlBeans and other types of objects, such as a control
        // factory.
        //
        String beanID = null;
        if (targetChild instanceof ControlBean)
        {
            //
            //
            //
            ControlBean bean = (ControlBean)targetChild;
            beanID = bean.getLocalID();

            //
            // The bean is anonymous, so we must generate a new unique name within this context.
            //
            if (beanID == null)
            {
                beanID = generateUniqueID(bean.getClass());
                bean.setLocalID(beanID);
            }

            ControlBean existingBean = (ControlBean)_childMap.get(beanID);
            if (existingBean != null && existingBean != targetChild)
            {
                throw new IllegalArgumentException("Attempting to add control with duplicate ID: " +
                                                   beanID);
            }
        }
        boolean added = super.add(targetChild);
        if (added && beanID != null)
            _childMap.put(beanID, targetChild);

        return added;
    }

    /**
     * Overrides the BeanContextSupport.remove() method to perform additional post-processing
     * on child removal.
     */
    public boolean remove(Object targetChild)
    {
        assert targetChild instanceof ControlBean;  // should be guaranteed above
        boolean removed = super.remove(targetChild);
        if (removed)
        {  
            //
            // Remove from the locally maintained child map
            //
            String localID = ((ControlBean)targetChild).getLocalID();
            Object removedChild = _childMap.remove(localID);
            assert removedChild == targetChild;     // just being safe
        }
        return removed;
    }

    /**
     * Returns a ControlBean instance nested the current BeanContext.
     * @param id the identifier for the target control, relative to the current
     *           context.
     */
    public ControlBean getBean(String id)
    {
        // If no control id separator found, the bean is a direct child of this context
        int delim = id.indexOf(org.apache.beehive.controls.api.bean.ControlBean.IDSeparator);
        if (delim < 0// child is a direct descendent
            return (ControlBean)_childMap.get(id);

        // Find the child referenced by the first element in the path
        ControlBean bean = (ControlBean)_childMap.get(id.substring(0, delim));
        if (bean == null)
            return bean;

        // Get the BeanContext associated with the found child, and then ask it
        // to resolve the rest of the path
        return ((ControlBeanContext)bean.getBeanContextProxy()).getBean(id.substring(delim+1));
    }

    /**
     * Returns the ControlBean associated directly with the ControlBeanContext.  If the
     * context represents a top-level container, will return null.
     */
    public ControlBean getControlBean()
    {
        return _bean;
    }

    public synchronized boolean hasSingleThreadedParent()
    {
        return _hasSingleThreadedParent;
    }

    /**
     *  Returns true if this container associated with this context service enforces
     *  single-threaded invocation, false otherwise.
     *
     *  This MUST be overridden by container-specific subclasses.  If they guarantee
     *  single-threaded behavior (such as the EJB container), they should return true.
     */
    public synchronized boolean isSingleThreadedContainer()
    {
        return ( hasSingleThreadedParent() || ( _bean != null && _bean.hasSingleThreadedImpl() ));
    }

    /**
     * The initializeControl method is invoked when the implementation instance associated
     * with the context has been instantiated and initialized.
     */
    public void initializeControl()
    {
        //
        // Deliver the onCreate event to any register lifecycle listeners
        //
        if (_lifeCycleListeners != null)
        {
            Iterator iter = _lifeCycleListeners.iterator();     
            while (iter.hasNext())
            {
                LifeCycle listener = (LifeCycle)iter.next();
                listener.onCreate();
            }
        }
    }

    /**
     * Returns the PropertyMap containing default properties for an AnnotatedElement
     * in the current context.  The initialization of PropertyMap binding is always
     * done by delegating to a ControlContainerContext, enabling containers to implement
     * property binding mechanisms (such as external config) that may override the values
     * contained within the element annotations.
     */
    public PropertyMap getAnnotationMap(AnnotatedElement annotElem)
    {
        ControlBeanContext beanContext = this;
        while (beanContext != null)
        {
            // REVIEW: should ControlContainerContext-derived classes override getBeanAnnotationMap?  Not sure
            // that name makes sense, and perhaps it shouldn't take a ControlBean.
            if (beanContext instanceof ControlContainerContext)
                return beanContext.getBeanAnnotationMap(_bean, annotElem);
            beanContext = (ControlBeanContext)beanContext.getBeanContext();
        }
           
        // No ControlContainerContext was found, so just use the default implementation
        return getBeanAnnotationMap(_bean, annotElem);
    }

    /**
     * The default implementation of getBeanAnnotationMap.  This returns a map based purely
     * upon annotation reflection
     */
    protected PropertyMap getBeanAnnotationMap(ControlBean bean, AnnotatedElement annotElem)
    {
        PropertyMap map = new AnnotatedElementMap(annotElem);

        // REVIEW: is this the right place to handle the general control client case?
        if ( bean != null )
            setDelegateMap( map, bean, annotElem );
       
        return map;
    }

    static protected void setDelegateMap( PropertyMap map, ControlBean bean, AnnotatedElement annotElem )
    {
        //
        // If building an annotation map for a method or field, we want to delegate back
        // to the base control type.
        //
        Class annotClass = null;
        if (annotElem instanceof Field)
        {
           annotClass = ((Field)annotElem).getType();
        }
        else if (annotElem instanceof Method)
        {
           annotClass = bean.getControlInterface();
        }
        if (annotClass != null)
        {
            PropertyMap delegateMap = bean.getAnnotationMap(annotClass);
            map.setDelegateMap(delegateMap);
        }
    }

    /**
     * Returns the default binding based entirely upon annotations or naming conventions.
     */
    static public String getDefaultControlBinding(Class controlIntf)
    {
        controlIntf = ControlBean.getMostDerivedInterface(controlIntf);

        ControlInterface intfAnnot =
                    (ControlInterface)controlIntf.getAnnotation(ControlInterface.class);
        String implBinding = intfAnnot.defaultBinding();
        implBinding = resolveDefaultBinding( implBinding, controlIntf.getName() );

        return implBinding;
    }

    /**
     * Implements the default control implementation binding algorithm ( <InterfaceName> + "Impl" ).  See
     * documentation for the org.apache.beehive.controls.api.bean.ControlInterface annotation.
     *
     * @param implBinding the value of the defaultBinding attribute returned from a ControlInterface annotation
     * @param controlClass the actual name of the interface decorated by the ControlInterface annotation
     * @return the resolved defaultBinding value
     */
    public static String resolveDefaultBinding( String implBinding, String controlClass )
    {
        int intfIndex = implBinding.indexOf(ControlInterface.INTERFACE_NAME);
        if (intfIndex >= 0)
        {
            implBinding = implBinding.substring(0,intfIndex) + controlClass +
                          implBinding.substring(intfIndex +
                                                ControlInterface.INTERFACE_NAME.length());
        }
        return implBinding;
    }

    //
    // ControlBeanContext.getControlInterface
    //
    public Class getControlInterface()
    {
        return _bean.getControlInterface();
    }

    //
    // ControlBeanContext.getControlPropertySet
    //
    public <T extends Annotation> T getControlPropertySet(Class<T> propertySet)
    {
        PropertyMap map = _bean.getPropertyMap();

        //
        // Optional properties are not exposed to clients using traditional JavaBean
        // setters/getters (because there is way to represent an 'unset' value); for
        // these properties, the impl can tell if the PropertySet is unset because
        // this method will return null.
        //
        if (!map.containsPropertySet(propertySet))
        {
            PropertySet psAnnot = propertySet.getAnnotation(PropertySet.class);
            if (psAnnot.optional())
                return null;
        }

        //
        // Construct a new PropertySet proxy instance that derives its values from
        // the bean property map.
        //
        return PropertySetProxy.getProxy(propertySet, map);
    }

    //
    // ControlBeanContext.getMethodPropertySet
    //
    public <T extends Annotation> T getMethodPropertySet(Method m, Class<T> propertySet)
    {
        PropertyMap map = _bean.getAnnotationMap(m);

        //
        // Optional properties are not exposed to clients using traditional JavaBean
        // setters/getters (because there is way to represent an 'unset' value); for
        // these properties, the impl can tell if the PropertySet is unset because
        // this method will return null.
        //
        if (!map.containsPropertySet(propertySet))
        {
            PropertySet psAnnot = propertySet.getAnnotation(PropertySet.class);
            if (psAnnot.optional())
                return null;
        }

        //
        // Construct a new PropertySet proxy instance that derives its values from
        // the method property map.
        //
        return PropertySetProxy.getProxy(propertySet, _bean.getAnnotationMap(m));
    }

    //
    // ControlBeanContext.getParameterPropertySet
    //
    public <T extends Annotation> T getParameterPropertySet(Method m, int i, Class<T> propertySet)
                                    throws IllegalArgumentException, IndexOutOfBoundsException
    {
        if (i >= m.getParameterTypes().length)
            throw new IndexOutOfBoundsException("Invalid parameter index for method:" + m);

        //
        // BUGBUG: Currently, there is no external override mechanism for method parameters
        //
        Annotation [] paramAnnots = m.getParameterAnnotations()[i];
        for (int j = 0; j < paramAnnots.length; j++)
            if (propertySet.isAssignableFrom(paramAnnots[j].getClass()))
                return (T)paramAnnots[j];

        return null;
    }

    //
    // ControlBeanContext.getParameterNames
    //
    public String [] getParameterNames(Method m) throws IllegalArgumentException
    {
        return _bean.getParameterNames(m);
    }

    //
    // ControlBeanContext.getNamedParameterValue
    //
    public Object getParameterValue(Method m, String parameterName, Object [] parameters)
                  throws IllegalArgumentException
    {
        String [] names = getParameterNames(m);   

        // Validate the input parameter array
        if (parameters.length != names.length)
            throw new IllegalArgumentException("Expected " + names.length + " parameters," +
                                               "Found " + parameters.length);

        // Finding the index of the matching parameter name
        int i = 0;
        while (i < names.length)
        {
            if (names[i].equals(parameterName))
                break;
            i++;
        }
        if (i == names.length)
            throw new IllegalArgumentException("No method parameter with name: " + parameterName);

        // Return the parameter value at the matched index
        return parameters[i];
    }

    //
    // ControlBeanContext.getPropertyMap
    //
    public PropertyMap getControlPropertyMap()
    {
        //
        // Return a wrapped copy of the original bean property map, so any edits
        // don't impact the bean properties.
        //
        return new BeanPropertyMap(_bean.getPropertyMap());
    }

    //
    // ControlBeanContext.getService
    //
    public <T> T getService(Class<T> serviceClass, Object selector)
    {
        //
        // If the requested service is a ControlBeanContext instance, the current instance
        // can be returned.
        //
        if (serviceClass.equals(org.apache.beehive.controls.api.context.ControlBeanContext.class))
            return (T)this;

        //
        // The parent BeanContext is responsible for providing requested services.  If
        // no parent context is available or it is does not manage services, then no service.
        //
        BeanContext bc = getBeanContext();
        if (bc == null || !(bc instanceof BeanContextServices))
            return null;

        //
        // Call getService on the parent context, using this bean as the requestor and the
        // this context as the child context and ServicesRevoked event listener parameters.
        //
        try
        {
            return  (T)((BeanContextServices)bc).getService(this, _bean, serviceClass, selector,
                                                            this);
        }
        catch (TooManyListenersException tmle)
        {
            // This would be highly unusual... it implies that the registration for service
            // revocation notifications failed for some reason.
            throw new ControlException("Unable to register for service events", tmle);
        }
    }

    //
    // ControlBeanContext.getControlHandle
    //
    public ControlHandle getControlHandle()
    {
        //
        // Find the associated ControlContainerContext, which provides a container-specific
        // implementation of ControlHandle
        //
        ControlBeanContext beanContext = this;
        while (beanContext != null && !(beanContext instanceof ControlContainerContext))
            beanContext = (ControlBeanContext)beanContext.getBeanContext();

        if (beanContext == null)
            return null;

        //
        // Ask the container for a ControlHandle instance referencing the target bean
        //
        return ((ControlContainerContext)beanContext).getControlHandle(_bean);
    }

    //
    // ControlBeanContext.getClassLoader
    //
    public java.lang.ClassLoader getClassLoader()
    {
        return getControlInterface().getClassLoader();
    }

    /**
     * Filename that contains ordering priority for controls interceptor services.
     * Each line in the file is a fully qualified interface name.  1st line in the file
     * is highest priority.
     */
    public static final String INTERCEPTOR_CONFIG_FILE = "controls-interceptors.config";

    /**
     * Applies externally defined (via INTERCEPTOR_CONFIG_FILE) ordering priority for
     * controls interceptor services.
     *
     * @param interceptors
     * @return String[]
     */
    public static String[] prioritizeInterceptors( String [] interceptors )
    {
        if ( interceptors == null )
            return null;

        // Read external configuration to obtain desired prioritization.
        if ( _interceptorPriorities == null )
        {
            // Only ever attempt to read the external configuration once; bounce the VM if you want to try again.
            _interceptorPriorities = new ArrayList<String>();
            BufferedReader in = null;
            try
            {
                InputStream configFileStream =
                    ControlBeanContext.class.getClassLoader().getResourceAsStream( INTERCEPTOR_CONFIG_FILE );

                if ( configFileStream != null )
                {
                    in = new BufferedReader(new InputStreamReader(configFileStream));
                    String str;
                    while ((str = in.readLine()) != null)
                        _interceptorPriorities.add(str);
                }
            }
            catch (IOException e)
            {
            }
            finally
            {
                try
                {
                    if (in != null)
                        in.close();
                }
                catch ( IOException ie ) { }
            }
        }

        // Put input list of interceptors into a Set for easy lookup
        Set<String> input = new HashSet<String>();
        for ( String ii : interceptors )
            input.add( ii );

        // Scan through priorities list, building a prioritized list
        ArrayList<String> prioritized = new ArrayList<String>(interceptors.length);
        for ( String p : _interceptorPriorities )
        {
            if ( input.contains(p) )
            {
                input.remove(p);
                prioritized.add(p);
            }
        }

        // Anything still left in the input set did not have a priority associated with it,
        // so they just go at the end in arbitrary order.
        for ( String p : input )
            prioritized.add(p);

        return prioritized.toArray(new String[prioritized.size()]);
    }

    //
    // ControlBeanContext.addLifeCycleListener
    //
    synchronized public void addLifeCycleListener(LifeCycle listener)
    {
        if (_lifeCycleListeners == null)
        {
            _lifeCycleListeners = new Vector<LifeCycle>();

            //
            // Since bound/constrained property changes are exposed as lifecycle events, we
            // need to register ourselves as a listener for these events the first time a
            // lifecycle listener is added.
            //
            _bean.getPropertyChangeSupport().addPropertyChangeListener(this);
            _bean.getVetoableChangeSupport().addVetoableChangeListener(this);
        }
        _lifeCycleListeners.addElement(listener);
    }

    //
    // ControlBeanContext.removeLifeCycleListener
    //
    synchronized public void removeLifeCycleListener(LifeCycle listener)
    {
        if (_lifeCycleListeners != null)
            _lifeCycleListeners.removeElement(listener);
    }

    //
    // PropertyChangeListener.propertyChange
    //
    public void propertyChange(PropertyChangeEvent pce)
    {
        if (_lifeCycleListeners != null)
        {
            Iterator iter = _lifeCycleListeners.iterator();     
            while (iter.hasNext())
            {
                LifeCycle listener = (LifeCycle)iter.next();
                listener.onPropertyChange(pce);
            }
        }
    }

    //
    // VetoableChangeListener.vetoableChange
    //
    public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException
    {
        if (_lifeCycleListeners != null)
        {
            Iterator iter = _lifeCycleListeners.iterator();     
            while (iter.hasNext())
            {
                LifeCycle listener = (LifeCycle)iter.next();
                listener.onVetoableChange(pce);
            }
        }
    }

    /* package */ String getControlID()
    {
        if (_controlID != null || _bean == null)
            return _controlID;

        // Initially set to the local beans relative ID
        String id = _bean.getLocalID();

        // If there is a parent context, prepend its ID and the ID separator
        BeanContext bc = getBeanContext();
        if (bc != null && bc instanceof ControlBeanContext)
        {
            String parentID = ((ControlBeanContext)bc).getControlID();
            if (parentID != null)
            {
                id = parentID +
                     org.apache.beehive.controls.api.bean.ControlBean.IDSeparator +
                     id;
            }
        }

        // Cache the computed value
        _controlID = id;

        return id;
    }

    /**
     * Resets the composite control ID for this context and all children beneath it.  This
     * can be used to invalidate cached values when necessary (for example, when a context
     * is reparented).
     */
    private void resetControlID()
    {
        _controlID = null;
        Iterator childIter = this.iterator();
        while (childIter.hasNext())
        {
            Object child = childIter.next();
            if (child instanceof ControlBeanContext)
                ((ControlBeanContext)child).resetControlID();
        }
    }

    //
    // BeanContextServices.getCurrentServiceClasses
    // Override the default implementatino of getCurrentServiceClasses inherited from
    // java.beans.beancontext.BeanContextServicesSuppport.  The reason for this is a bug/
    // flaw in its underlying implementation.  It does not include any services exposed
    // by any nesting contexts.  This is contradictory to the implementation of hasService()
    // and getService() which do delegate up to a parent context to find services if not
    // available on the local context.  This means hasService() could return 'true' for a
    // service that isn't returned by getCurrentServiceClasses(), which seems like a bug.
    //
    synchronized public Iterator getCurrentServiceClasses()
    {
        Set classSet = new HashSet();
        BeanContextServices bcs = this;

        while (bcs != null)
        {
            // Optimize the case where we can do a direct copy based upon impl knowledge.
            if (bcs instanceof ControlBeanContext)
            {
                classSet.addAll(((ControlBeanContext)bcs).services.keySet());
            }
            else
            {
                Iterator iter = bcs.getCurrentServiceClasses();
                while (iter.hasNext())
                    classSet.add(iter.next());
            }
           
            // Go up to the parent, if it is a service provider as well
            BeanContext bc = getBeanContext();
            if (bc instanceof BeanContextServices && bcs != bc)
                bcs = (BeanContextServices)bc;
            else
                bcs = null;
        }
        return classSet.iterator();
    }

    //
    // BeanContextServices.getCurrentServiceSelectors
    // Override getCurrentServiceSelectors for the same reason as above
    //
    public Iterator getCurrentServiceSelectors(Class serviceClass)
    {
        if (hasService(serviceClass))
            return super.getCurrentServiceSelectors(serviceClass);
       
        BeanContext bc = getBeanContext();
        if (bc instanceof BeanContextServices)
            return ((BeanContextServices)bc).getCurrentServiceSelectors(serviceClass);

        return null;
    }

    /**
     * The ControlBean instance that this context is providing services for.  This value can
     * be null, if the context instance is associated with top-level (non-control) context.
     */
    private ControlBean _bean;

    /**
     * Indicates whether this context's parent guarantees single-threaded behaviour.
     */
    private boolean _hasSingleThreadedParent = false;

    /**
     * Maps children by the local (relative) ID of the child to the actual bean instance.
     * This needs to be synchronized, because adds/removes/gets are not necessarily guaranteed
     * to happen within the scope of the global hierarchy lock.   It would be relatively easy
     * to synchronize add/remove, since setBeanContext on the child is inside this lock scope,
     * but gets on the map are another story.
     */
    private Map<String,Object> _childMap =
                Collections.synchronizedMap(new HashMap<String,Object>());

    /**
     * Maintains a set of NameGenerators (for control ID generation) keyed by a
     * base prefix.  The map itself is lazily constructed, so there is minimal
     * overhead of no id generation is needed in this context.
     */
    private Map<String,NameGenerator> _nameGenerators;

    /**
     * Maintains the list of lifecycle event listeners (if any) for this context.
     */
    transient private Vector<LifeCycle> _lifeCycleListeners;

    /**
     * Caches the full composite control ID, that includes the entire path from the root
     * ContainerContext to the associated bean.  This value can be transient, since it
     * can be easily recomputed when needed.
     */
    transient private String _controlID;

    private static ArrayList<String> _interceptorPriorities;
}
TOP

Related Classes of org.apache.beehive.controls.runtime.bean.ControlBeanContext

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.