org.apache.beehive.controls.runtime.bean.ControlBean Maven / Gradle / Ivy
/*
* 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.
*
* $Header:$
*/
package org.apache.beehive.controls.runtime.bean;
import java.beans.beancontext.BeanContext;
import java.beans.beancontext.BeanContextServices;
import java.beans.PropertyChangeSupport;
import java.beans.VetoableChangeSupport;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.Semaphore;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TooManyListenersException;
import java.util.Vector;
import org.apache.beehive.controls.api.ControlException;
import org.apache.beehive.controls.api.properties.BaseProperties;
import org.apache.beehive.controls.api.properties.AnnotatedElementMap;
import org.apache.beehive.controls.api.properties.PropertyMap;
import org.apache.beehive.controls.api.properties.BeanPropertyMap;
import org.apache.beehive.controls.api.properties.PropertyKey;
import org.apache.beehive.controls.api.properties.PropertySetProxy;
import org.apache.beehive.controls.api.versioning.VersionRequired;
import org.apache.beehive.controls.api.versioning.Version;
import org.apache.beehive.controls.api.bean.Threading;
import org.apache.beehive.controls.api.bean.ThreadingPolicy;
import org.apache.beehive.controls.api.bean.ControlImplementation;
import org.apache.beehive.controls.api.context.ControlThreadContext;
import org.apache.beehive.controls.api.events.EventRef;
import org.apache.beehive.controls.api.events.EventSet;
import org.apache.beehive.controls.spi.context.ControlBeanContextFactory;
import org.apache.beehive.controls.spi.svc.Interceptor;
import org.apache.beehive.controls.spi.svc.InterceptorPivotException;
import org.apache.commons.discovery.tools.DiscoverClass;
/**
* The ControlBean class is an abstract base class for the JavaBean classes generated to support
* Beehive Controls.
*
* The ControlBean class indirectly implements BeanContextProxy; the
* {@link org.apache.beehive.controls.api.context.ControlBeanContext} that it contains/scopes acts as that proxy.
*
* All support APIs (which may be called from derived subclasses or contextual services)
* are generally marked as protected and have names that start with an underscore. This avoids the
* possibility that the name might conflict with a user-defined method on a control's public or
* extended Control interfaces.
*
* NOTE: Adding public methods should be done with great care; any such method becomes part of the
* public API, and occupies the method namespace for all controls.
*/
abstract public class ControlBean
implements org.apache.beehive.controls.api.bean.ControlBean
{
/**
*
* @param context the containing ControlBeanContext. May be null, in which case the bean will attempt to
* associate with an active context at runtime (via thread-locals).
* @param id the local ID for the control, scoped to the control bean context.
* @param initProperties a PropertyMap containing initial properties for the control
* @param controlIntf the control interface or extension directly implemented by the control bean
*/
protected ControlBean(org.apache.beehive.controls.api.context.ControlBeanContext context,
String id,
PropertyMap initProperties,
Class controlIntf)
{
super();
_localID = id;
_controlIntf = controlIntf;
//
// If no containing context was specified during construction, see if there is a current
// active container context and implicitly associated the control with it.
//
if (context == null)
context = ControlThreadContext.getContext();
ControlBeanContextFactory cbcFactory = lookupControlBeanContextFactory(context);
_cbc = cbcFactory.instantiate(this);
//
// Associate this bean with the context. Beans may run without a context!
// Note that the add() call has the side-effect of calling ControlBean.setBeanContext(), which does
// additional setup work, so we make sure we always call that anyways!
//
if (context != null)
context.add(this);
else
setBeanContext(null);
//
// Get the default map for the control class. This contains the default property
// values for all beans of the class.
//
PropertyMap classMap = getAnnotationMap(controlIntf);
if (initProperties != null)
{
//
// If the initialization map derives its values from a Java annotated element,
// then allow container overrides on this element to be applied. This will also
// coelesce maps referencing the same element.
//
AnnotatedElement annotElem = null;
if (initProperties instanceof AnnotatedElementMap)
{
annotElem = ((AnnotatedElementMap)initProperties).getAnnotatedElement();
initProperties = getAnnotationMap(annotElem);
}
//
// If an initial property map was provided, set it to delegate to the default
// map, and then create a wrapper map around it for storing per instance
// properties.
//
if (annotElem != controlIntf)
initProperties.setDelegateMap(classMap);
_properties = new BeanPropertyMap(initProperties);
}
else
{
//
// If no initial map was provided, simply create an empty map wrapping the
// control class default.
//
_properties = new BeanPropertyMap(classMap);
}
}
/**
* Configure this bean to be thread-safe given the threading settings of the impl class and
* the outer container.
*/
private void ensureThreadingBehaviour()
{
//
// If the implementation class requires a guarantee of single-threaded behavior and the
// outer container does not guarantee it, then enable invocation locking on this
// bean instance.
//
if (hasSingleThreadedImpl() && ! _cbc.hasSingleThreadedParent())
_invokeLock = new Semaphore(1, true);
else
_invokeLock = null;
}
/**
* Return the BeanContextService proxy associated with this bean instance
*/
final public ControlBeanContext getBeanContextProxy()
{
return _cbc;
}
/**
* Returns the nesting BeanContext for this ControlBean. This is thread-safe even though it
* is not synchronized.
*/
final public BeanContext getBeanContext()
{
//
// Indirect through the bean proxy for this control bean and up to its parent nesting
// context. Both these calls (getBeanContextProxy() and getBeanContext()) are, and must
// remain, thread-safe.
//
return getBeanContextProxy().getBeanContext();
}
/**
* Called by the BeanContextProxy (_cbc) whenever the _parent_ context containing this control bean is
* changed. This is the place to do any initialization (or reinitialization) that is dependent
* upon attributes of the container for the ControlBean.
*
* Note: this is called in the ControlBean ctor, when a parent context calls add() on the nascent
* bean.
*
* @param bc the new parent context containing this control bean (not _cbc)
*/
final public synchronized void setBeanContext(BeanContext bc)
{
ensureThreadingBehaviour();
}
/**
* Returns the control ID for this control
*/
final public String getControlID()
{
return _cbc.getControlID();
}
/**
* Returns the public interface for this control.
*/
final public Class getControlInterface()
{
return _controlIntf;
}
/**
* Returns true if the implementation class for this ControlBean requires the framework
* to ensure thread-safety for it.
*/
/*package*/ boolean hasSingleThreadedImpl()
{
return _threadingPolicy == ThreadingPolicy.SINGLE_THREADED;
}
/**
* Obtains an instance of the appropriate ImplInitializer class
*/
protected synchronized ImplInitializer getImplInitializer()
{
if (_implInitializer == null)
{
try
{
Class initClass = _implClass.getClassLoader().loadClass(
_implClass.getName() + "Initializer");
_implInitializer = (ImplInitializer)initClass.newInstance();
}
catch (Exception e)
{
throw new ControlException("Control initialization failure", e);
}
}
return _implInitializer;
}
/**
* Returns the target control instance associated with this ControlBean, performing lazy
* instantiation and initialization of the instance.
*
* REVIEW: could probably improve the granularity of locking here, but start w/ just
* synchronizing the entire fn.
*/
public synchronized Object ensureControl()
{
if (_control == null)
{
//
// See if the property map specifies an implementation class for the control;
// if not, use default binding.
//
String implBinding = null;
BaseProperties bp = _properties.getPropertySet( BaseProperties.class );
if ( bp != null )
implBinding = bp.controlImplementation();
else
implBinding = ControlUtils.getDefaultControlBinding(_controlIntf);
try
{
_implClass = _controlIntf.getClassLoader().loadClass(implBinding);
//
// Validate that the specified implementation class has an @ControlImplementation
// annotation, else downstream requirements (such as having a valid control init
// class) will not be met.
//
if (_implClass.getAnnotation(ControlImplementation.class) == null)
{
throw new ControlException("@" + ControlImplementation.class.getName() + " annotation is missing from control implementation class: " + _implClass.getName());
}
}
catch (ClassNotFoundException cnfe)
{
throw new ControlException("Unable to load control implementation: " + implBinding, cnfe);
}
//
// Cache the threading policy associated with the impl
//
Threading thr = (Threading)_implClass.getAnnotation(Threading.class);
if ( thr != null )
_threadingPolicy = thr.value();
else
_threadingPolicy = ThreadingPolicy.SINGLE_THREADED; // default to single-threaded
ensureThreadingBehaviour();
try
{
//
// Create and initialize the new instance
//
_control = _implClass.newInstance();
try
{
/*
Run the ImplInitializer. This class is code generated based on metadata from a control
implementation. If a Control implementation declares event handlers for the
ControlBeanContext or for the ResourceContext, executing this code generated class
will add the appropriate LifeCycle and / or Resource event listeners.
*/
getImplInitializer().initialize(this, _control);
_hasServices = true;
}
catch (Exception e)
{
throw new ControlException("Control initialization failure", e);
}
//
// Once the control is initialized, then allow the associated context
// to do any initialization.
//
ControlBeanContext cbcs = getBeanContextProxy();
/*
Implementation note: this call will run the LifeCycleListener(s) that have
been wired-up to the ControlBeanContext object associated with this ControlBean.
*/
cbcs.initializeControl();
}
catch (RuntimeException re) {
// never mask RuntimeExceptions
throw re;
}
catch (Exception e)
{
throw new ControlException("Unable to create control instance", e);
}
}
//
// If the implementation instance does not currently have contextual services, they
// are lazily restored here.
//
if (!_hasServices)
{
getImplInitializer().initServices(this, _control);
_hasServices = true;
}
return _control;
}
/**
* Returns the implementation instance associated with this ControlBean.
*/
/* package */ Object getImplementation() {
return _control;
}
/**
* The preinvoke method is called before all operations on the control. In addition to
* providing a basic hook for logging, context initialization, resource management,
* and other common services, it also provides a hook for interceptors.
*/
protected void preInvoke(Method m, Object [] args, String [] interceptorNames)
throws InterceptorPivotException
{
//
// If the implementation expects single threaded behavior and our container does
// not guarantee it, then enforce it locally here
//
if (_invokeLock != null)
{
try { _invokeLock.acquire(); } catch (InterruptedException ie) { }
}
//
// Process interceptors
//
if ( interceptorNames != null )
{
for ( String n : interceptorNames )
{
Interceptor i = ensureInterceptor( n );
try
{
i.preInvoke( this, m, args );
}
catch (InterceptorPivotException ipe)
{
ipe.setInterceptorName(n);
throw ipe;
}
}
}
Vector invokeListeners = getInvokeListeners();
if (invokeListeners.size() > 0)
{
for (InvokeListener listener : invokeListeners)
listener.preInvoke(m, args);
}
}
/**
* The preinvoke method is called before all operations on the control. It is the basic
* hook for logging, context initialization, resource management, and other common
* services
*/
protected void preInvoke(Method m, Object [] args)
{
try
{
preInvoke(m, args, null);
}
catch (InterceptorPivotException ipe)
{
//this will never happen because no interceptor is passed.
}
}
/**
* The postInvoke method is called after all operations on the control. In addition to
* providing the basic hook for logging, context initialization, resource management, and other common
* services, it also provides a hook for interceptors. During preInvoke, interceptors will be
* called in the order that they are in the list. During postInvoke, they will be called in the
* reverse order. Here is an example of the call sequence with I1, I2, and I3 being interceptors in the list:
*
* I1.preInvoke() -> I2.preInvoke() -> I3.preInvoke() -> invoke method
* |
* I1.postInvoke() <- I2.postInvoke() <- I3.postInvoke() <---
*
* In the event that an interceptor in the list pivoted during preInvoke, the "pivotedInterceptor"
* parameter indicates the interceptor that had pivoted, and the interceptors succeeding it in the list will
* not be called during postInvoke.
*/
protected void postInvoke(Method m, Object [] args, Object retval, Throwable t, String [] interceptorNames, String pivotedInterceptor)
{
try
{
Vector invokeListeners = getInvokeListeners();
if (invokeListeners.size() > 0)
{
for (InvokeListener listener : invokeListeners)
listener.postInvoke(retval, t);
}
//
// Process interceptors
//
if ( interceptorNames != null )
{
for (int cnt = interceptorNames.length-1; cnt >= 0; cnt-- )
{
String n = interceptorNames[cnt];
if (pivotedInterceptor == null || n.equals(pivotedInterceptor))
{
pivotedInterceptor = null;
Interceptor i = ensureInterceptor( n );
i.postInvoke( this, m, args, retval, t );
}
}
}
}
finally
{
//
// Release any lock obtained above in preInvoke
//
if (_invokeLock != null)
_invokeLock.release();
}
}
/**
* The postInvoke method is called after all operations on the control. It is the basic
* hook for logging, context initialization, resource management, and other common
* services.
*/
protected void postInvoke(Method m, Object [] args, Object retval, Throwable t)
{
postInvoke(m, args, retval, t, null, null);
}
/**
* Sets the EventNotifier for this ControlBean
*/
protected void setEventNotifier(Class eventSet, T notifier)
{
_notifiers.put(eventSet,notifier);
//
// Register this notifier for all EventSet interfaces up the interface inheritance
// hiearachy as well
//
List superEventSets = new ArrayList();
getSuperEventSets(eventSet, superEventSets);
Iterator i = superEventSets.iterator();
while (i.hasNext())
{
Class superEventSet = i.next();
_notifiers.put(superEventSet,notifier);
}
}
/**
* Finds all of the EventSets extended by the input EventSet, and adds them to
* the provided list.
* @param eventSet
* @param superEventSets
*/
private void getSuperEventSets(Class eventSet, List superEventSets)
{
Class[] superInterfaces = eventSet.getInterfaces();
if (superInterfaces != null)
{
for (int i=0; i < superInterfaces.length; i++)
{
Class superInterface = superInterfaces[i];
if (superInterface.isAnnotationPresent(EventSet.class))
{
superEventSets.add(superInterface);
// Continue traversing up the EventSet inheritance hierarchy
getSuperEventSets(superInterface, superEventSets);
}
}
}
}
/**
* Returns an EventNotifier/UnicastEventNotifier for this ControlBean for the target event set
*/
protected T getEventNotifier(Class eventSet)
{
return (T)_notifiers.get(eventSet);
}
/**
* Returns the list of InvokeListeners for this ControlBean
*/
/* package */ Vector getInvokeListeners()
{
if (_invokeListeners == null)
_invokeListeners = new Vector();
return _invokeListeners;
}
/**
* Registers a new InvokeListener for this ControlBean.
*/
/* package */ void addInvokeListener(InvokeListener invokeListener)
{
getInvokeListeners().addElement(invokeListener);
}
/**
* Deregisters an existing InvokeListener for this ControlBean.
*/
/* package */ void removeInvokeListener(InvokeListener invokeListener)
{
getInvokeListeners().removeElement(invokeListener);
}
/**
* Returns the local (parent-relative) ID for this ControlBean
*/
protected String getLocalID()
{
return _localID;
}
/**
* Set the local (parent-relative) ID for this ControlBean. It has package access because
* the local ID should only be set from within the associated context, and only when the
* bean is currently anonymous (hence the assertion below)
*/
/* package */ void setLocalID(String localID)
{
assert _localID == null; // should only set if not already set!
_localID = localID;
}
/**
* Returns the bean context instance associated with the this bean, as opposed to the
* parent context returned by the public getBeanContext() API.
*/
public ControlBeanContext getControlBeanContext()
{
//
// The peer context instance is the context provider for this ControlBean
//
return getBeanContextProxy();
}
/**
* Locates and obtains a context service from the BeanContextServices instance
* supporting this bean.
*
* The base design for the BeanContextServicesSupport is that it will delegate up to peers
* in a nesting context, so a nested control bean will look 'up' to find a service provider.
*/
protected Object getControlService(Class serviceClass, Object selector)
throws TooManyListenersException
{
//
// Get the associated context object, then use it to locate the (parent) bean context.
// Services are always provided by the parent context.
//
ControlBeanContext cbc = getControlBeanContext();
BeanContext bc = cbc.getBeanContext();
if (bc == null || !(bc instanceof BeanContextServices))
throw new ControlException("Can't locate service context: " + bc);
//
// Call getService on the parent context, using this bean as the requestor and the
// associated peer context instance as the child and event listener parameters.
//
return ((BeanContextServices)bc).getService(cbc, this, serviceClass, selector, cbc);
}
/**
* Sets a property on the ControlBean instance. All generated property setter methods
* will delegate down to this method.
*/
protected void setControlProperty(PropertyKey key, Object o)
{
AnnotationConstraintValidator.validate(key, o);
_properties.setProperty(key, o);
}
/**
* Dispatches the requested operation event on the ControlBean.
* @see org.apache.beehive.controls.runtime.bean.ControlContainerContext#dispatchEvent
*/
/* package */ Object dispatchEvent(EventRef event, Object [] args)
throws IllegalAccessException,IllegalArgumentException,
InvocationTargetException
{
ensureControl();
//
// Translate the EventRef back to an actual event method on the ControlInterface
//
Class controlInterface = getControlInterface();
Method method = event.getEventMethod(controlInterface);
//
// Locate the target of the event
//
Object eventTarget = null;
if (method.getDeclaringClass().isAssignableFrom(_control.getClass()))
{
//
// If the control implementation implements that EventSet interface, then
// dispatch the event directly to it, and allow it do make the decision about
// how/when to dispatch to any external listeners (via a @Client notifier
// instance)
//
eventTarget = _control;
}
else
{
//
// The associated control implementation does not directly handle the event,
// so find the event notifier instance for the EventSet interface associated
// with the method.
//
eventTarget = _notifiers.get(method.getDeclaringClass());
if (eventTarget == null)
throw new IllegalArgumentException("No event notifier found for " + event);
}
//
// Dispatch the event
//
return method.invoke(eventTarget, args);
}
/**
* Returns a property on the ControlBean instance. This version does not coerce
* an annotation type property from a PropertyMap to a proxy instance of the
* type.
*/
protected Object getRawControlProperty(PropertyKey key)
{
return _properties.getProperty(key);
}
/**
* Returns a property on the ControlBean instance. All generated property getter methods
* will delegate down to this method
*/
protected Object getControlProperty(PropertyKey key)
{
Object value = getRawControlProperty(key);
// If the held value is a PropertyMap, then wrap it in an annotation proxy of
// the expected type.
if (value instanceof PropertyMap)
{
PropertyMap map = (PropertyMap)value;
value = PropertySetProxy.getProxy(map.getMapClass(), map);
}
return value;
}
/* this method is implemented during code generation by a ControlBean extension */
/**
* Returns the local cache for ControlBean property maps.
*/
abstract protected Map getPropertyMapCache();
/**
* Returns the PropertyMap containing values associated with an AnnotatedElement. Elements
* that are associated with the bean's Control interface will be locally cached.
*/
protected PropertyMap getAnnotationMap(AnnotatedElement annotElem)
{
Map annotCache = getPropertyMapCache();
// If in the cache already , just return it
if (annotCache.containsKey(annotElem))
return (PropertyMap)annotCache.get(annotElem);
//
// Ask the associated ControlBeanContext to locate and initialize a PropertyMap, then
// store it in the local cache.
//
PropertyMap map = getControlBeanContext().getAnnotationMap(annotElem);
annotCache.put(annotElem, map);
return map;
}
/**
* Returns the property map containing the properties for the bean
*/
/* package */ BeanPropertyMap getPropertyMap()
{
return _properties;
}
/**
* This protected version is only available to concrete subclasses that expose bound
* property support. This method is synchronized to enable lazy instantiation, in
* the belief that it is a bigger win to avoid allocating when there are no listeners
* than it is to introduce synchronization overhead on access.
*/
synchronized protected PropertyChangeSupport getPropertyChangeSupport()
{
if (_changeSupport == null)
_changeSupport = new PropertyChangeSupport(this);
return _changeSupport;
}
/**
* Delivers a PropertyChangeEvent to any registered PropertyChangeListeners associated
* with the property referenced by the specified key.
*
* This method *should not* be synchronized, as the PropertyChangeSupport has its own
* built in synchronization mechanisms.
*/
protected void firePropertyChange(PropertyKey propertyKey, Object oldValue, Object newValue)
{
// No change support instance means no listeners
if (_changeSupport == null)
return;
_changeSupport.firePropertyChange(propertyKey.getPropertyName(), oldValue, newValue);
}
/**
* This protected version is only available to concrete subclasses that expose bound
* property support. This method is synchronized to enable lazy instantiation, in
* the belief that is a bigger win to avoid allocating when there are no listeners
* than it is to introduce synchronization overhead on access.
*/
synchronized protected VetoableChangeSupport getVetoableChangeSupport()
{
if (_vetoSupport == null)
_vetoSupport = new VetoableChangeSupport(this);
return _vetoSupport;
}
/**
* Delivers a PropertyChangeEvent to any registered VetoableChangeListeners associated
* with the property referenced by the specified key.
*
* This method *should not* be synchronized, as the VetoableChangeSupport has its own
* built in synchronization mechanisms.
*/
protected void fireVetoableChange(PropertyKey propertyKey, Object oldValue, Object newValue)
throws java.beans.PropertyVetoException
{
// No veto support instance means no listeners
if (_vetoSupport == null)
return;
_vetoSupport.fireVetoableChange(propertyKey.getPropertyName(), oldValue, newValue);
}
/**
* Returns the parameter names for a method on the ControlBean. Actual mapping is done
* by generated subclasses, so if we reach the base ControlBean implementation, then
* no parameter names are available for the target method.
*/
protected String [] getParameterNames(Method m)
{
throw new IllegalArgumentException("No parameter name data for " + m);
}
/**
* Computes the most derived ControlInterface for the specified ControlExtension.
* @param controlIntf
* @return the most derived ControlInterface
* @deprecated Use {@link ControlUtils#getMostDerivedInterface(Class)} instead. This method will
* be removed in the next release.
*/
public static Class getMostDerivedInterface(Class controlIntf)
{
return ControlUtils.getMostDerivedInterface(controlIntf);
}
/**
* Enforces the VersionRequired annotation at runtime (called from each ControlBean).
* @param intfName
* @param version
* @param versionRequired
*/
protected static void enforceVersionRequired(String intfName, Version version, VersionRequired versionRequired)
{
if ( versionRequired != null )
{
int majorRequired = versionRequired.major();
int minorRequired = versionRequired.minor();
if ( majorRequired < 0 ) // no real version requirement
return;
int majorPresent = -1;
int minorPresent = -1;
if ( version != null )
{
majorPresent = version.major();
minorPresent = version.minor();
if ( majorRequired <= majorPresent &&
(minorRequired < 0 || minorRequired <= minorPresent) )
{
// Version requirement is satisfied
return;
}
}
//
// Version requirement failed
//
throw new ControlException( "Control extension " + intfName + " fails version requirement: requires interface version " +
majorRequired + "." + minorRequired + ", found interface version " +
majorPresent + "." + minorPresent + "." );
}
}
/**
* Implementation of the Java serialization writeObject method
*/
private synchronized void writeObject(ObjectOutputStream oos)
throws IOException
{
if (_control != null)
{
//
// If the implementation class is marked as transient/stateless, then reset the
// reference to it prior to serialization. A new instance will be created by
// ensureControl() upon first use after deserialization.
// If the implementation class is not transient, then invoke the ImplInitializer
// resetServices method to reset all contextual service references to null, as
// contextual services should never be serializated and always reassociated on
// deserialization.
//
ControlImplementation implAnnot = (ControlImplementation)_implClass.getAnnotation(ControlImplementation.class);
assert implAnnot != null;
if (implAnnot.isTransient())
{
_control = null;
}
else
{
getImplInitializer().resetServices(this, _control);
_hasServices = false;
}
}
oos.defaultWriteObject();
}
/**
* Called during XMLDecoder reconstruction of a ControlBean.
*/
public void decodeImpl(Object impl)
{
if (impl != _control)
throw new ControlException("Cannot change implementation");
}
/**
* Internal method used to lookup a ControlBeanContextFactory. This factory is used to create the
* ControlBeanContext object for this ControlBean. The factory is discoverable from either the containing
* ControlBeanContext object or from the environment. If the containing CBC object exposes a
* contextual service of type {@link ControlBeanContextFactory}, the factory returned from this will
* be used to create a ControlBeanContext object.
*
* @param context
* @return the ControlBeanContextFactory discovered in the environment or a default one if no factory is configured
*/
private ControlBeanContextFactory lookupControlBeanContextFactory
(org.apache.beehive.controls.api.context.ControlBeanContext context) {
// first, try to find the CBCFactory from the container
if(context != null) {
ControlBeanContextFactory cbcFactory = context.getService(ControlBeanContextFactory.class, null);
if(cbcFactory != null) {
return cbcFactory;
}
}
// Create the context that acts as the BeanContextProxy for this bean (the context that this bean _defines_).
try
{
DiscoverClass discoverer = new DiscoverClass();
Class factoryClass =
discoverer.find(ControlBeanContextFactory.class, DefaultControlBeanContextFactory.class.getName());
return (ControlBeanContextFactory)factoryClass.newInstance();
}
catch (Exception e) {
throw new ControlException("Exception creating ControlBeanContext", e);
}
}
/**
* Retrieves interceptor instances, creates them lazily.
*/
protected Interceptor ensureInterceptor( String n )
{
Interceptor i = null;
if ( _interceptors == null )
{
_interceptors = new HashMap();
}
else
{
i = _interceptors.get( n );
}
if ( i == null )
{
try
{
i = (Interceptor) getControlService( getControlBeanContext().getClassLoader().loadClass( n ), null );
}
catch ( Exception e )
{
// Couldn't instantiate the desired service; usually this is because the service interface itself
// isn't present on this system at runtime (ClassNotFoundException), or if the container of the
// control didn't registers the service.
/* TODO log a message here to that effect, but just swallow the exception for now. */
}
finally
{
// We want to always return an interceptor, so if we can't get the one we want, we'll substitute
// a "null" interceptor that does nothing.
if ( i == null)
i = new NullInterceptor();
_interceptors.put( n, i );
}
}
return i;
}
/**
* The "null" interceptor that does nothing. Used when a specific interceptor
* is unavailable at runtime.
*/
static private class NullInterceptor
implements Interceptor
{
public void preInvoke( org.apache.beehive.controls.api.bean.ControlBean cb, Method m, Object [] args ) {}
public void postInvoke( org.apache.beehive.controls.api.bean.ControlBean cb, Method m, Object [] args, Object retval, Throwable t) {}
public void preEvent( org.apache.beehive.controls.api.bean.ControlBean cb, Class eventSet, Method m, Object [] args) {}
public void postEvent( org.apache.beehive.controls.api.bean.ControlBean cb, Class eventSet, Method m, Object [] args, Object retval, Throwable t ) {}
}
/** BEGIN unsynchronized fields */
/**
* The following fields are initialized in the constructor and never subsequently changed,
* so they are safe for unsynchronized read access
*/
/**
* The control implementation class bound to this ControlBean
*/
protected Class _implClass;
/**
* The threading policy associated with the control implementation wrapped by this
* ControlBean. Initialized to MULTI_THREADED in order to assume multi-threadedness
* until a bean is associated with a specific (potentially single-threaded) implementation.
*/
transient private ThreadingPolicy _threadingPolicy = ThreadingPolicy.MULTI_THREADED;
/**
* Contains the per-instance properties set for this ControlBean.
*/
private BeanPropertyMap _properties;
/** END unsynchronized fields */
/* BEGIN synchronized fields */
/*
* The following fields must be:
* 1) only written in synchronized methods or (unsynchronized) constructors
* 2) only read in synchronized methods or methods that are safe wrt the values changing during
* execution.
*/
/**
* The control implementation instance wrapped by this ControlBean
*/
private Object _control;
/**
* The control bean context instance associated with this ControlBean
*/
private ControlBeanContext _cbc;
/**
* An ImplInitializer instances used to initialize/reset the state of the associated
* implementation instance.
*/
transient private ImplInitializer _implInitializer;
/**
* Indicates whether the contextual services associated with the bean have been
* fully initialized.
*/
transient private boolean _hasServices = false;
/**
* Used to guarantee single threaded invocation when required. If the
* outer container provides the guarantee or the implementation itself
* is threadsafe, then the value will be null.
*/
transient private Semaphore _invokeLock;
/**
* This field manages PropertyChangeListeners (if supporting bound properties).
*/
private PropertyChangeSupport _changeSupport;
/**
* This field manages VetoabbleChangeListeners (if supporting constrained properties)
*/
private VetoableChangeSupport _vetoSupport;
/** END synchronized fields */
/**
* The (context relative) control ID associated with this instance
*/
private String _localID;
/**
* The public control interface associated with this ControlBean
*/
private Class _controlIntf;
/**
* This field manages the register listener list(s) associated with event set interfaces
* for the ControlBean. The value objects are either UnicastEventNotifier or EventNotifier
* instances, depending upon whether the associated EventSet interface is unicast or
* multicast.
*/
private HashMap _notifiers = new HashMap();
/**
* Maintains the list of callback event listeners (if any) for this ControlBean.
*/
transient private Vector _invokeListeners;
/**
* HashMap to hold interceptor impl instances.
* Populated lazily. Maps interceptor interface name to impl.
*/
transient private HashMap _interceptors;
}