0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: *
0017: * $Header:$
0018: */
0019: package org.apache.beehive.controls.runtime.bean;
0020:
0021: import java.beans.beancontext.BeanContext;
0022: import java.beans.beancontext.BeanContextServices;
0023: import java.beans.PropertyChangeSupport;
0024: import java.beans.VetoableChangeSupport;
0025: import java.io.IOException;
0026: import java.io.ObjectOutputStream;
0027: import java.lang.reflect.AnnotatedElement;
0028: import java.lang.reflect.InvocationTargetException;
0029: import java.lang.reflect.Method;
0030: import java.util.concurrent.Semaphore;
0031: import java.util.ArrayList;
0032: import java.util.HashMap;
0033: import java.util.Iterator;
0034: import java.util.List;
0035: import java.util.Map;
0036: import java.util.TooManyListenersException;
0037: import java.util.Vector;
0038:
0039: import org.apache.beehive.controls.api.ControlException;
0040: import org.apache.beehive.controls.api.properties.BaseProperties;
0041: import org.apache.beehive.controls.api.properties.AnnotatedElementMap;
0042: import org.apache.beehive.controls.api.properties.PropertyMap;
0043: import org.apache.beehive.controls.api.properties.BeanPropertyMap;
0044: import org.apache.beehive.controls.api.properties.PropertyKey;
0045: import org.apache.beehive.controls.api.properties.PropertySetProxy;
0046: import org.apache.beehive.controls.api.versioning.VersionRequired;
0047: import org.apache.beehive.controls.api.versioning.Version;
0048: import org.apache.beehive.controls.api.bean.Threading;
0049: import org.apache.beehive.controls.api.bean.ThreadingPolicy;
0050: import org.apache.beehive.controls.api.bean.ControlImplementation;
0051: import org.apache.beehive.controls.api.context.ControlThreadContext;
0052: import org.apache.beehive.controls.api.events.EventRef;
0053: import org.apache.beehive.controls.api.events.EventSet;
0054: import org.apache.beehive.controls.spi.context.ControlBeanContextFactory;
0055: import org.apache.beehive.controls.spi.svc.Interceptor;
0056: import org.apache.beehive.controls.spi.svc.InterceptorPivotException;
0057: import org.apache.commons.discovery.tools.DiscoverClass;
0058:
0059: /**
0060: * The ControlBean class is an abstract base class for the JavaBean classes generated to support
0061: * Beehive Controls.
0062: * <p>
0063: * The ControlBean class indirectly implements BeanContextProxy; the
0064: * {@link org.apache.beehive.controls.api.context.ControlBeanContext} that it contains/scopes acts as that proxy.
0065: * <p>
0066: * All support APIs (which may be called from derived subclasses or contextual services)
0067: * are generally marked as protected and have names that start with an underscore. This avoids the
0068: * possibility that the name might conflict with a user-defined method on a control's public or
0069: * extended Control interfaces.
0070: * <p>
0071: * NOTE: Adding public methods should be done with great care; any such method becomes part of the
0072: * public API, and occupies the method namespace for all controls.
0073: */
0074: abstract public class ControlBean implements
0075: org.apache.beehive.controls.api.bean.ControlBean {
0076: /**
0077: *
0078: * @param context the containing ControlBeanContext. May be null, in which case the bean will attempt to
0079: * associate with an active context at runtime (via thread-locals).
0080: * @param id the local ID for the control, scoped to the control bean context.
0081: * @param initProperties a PropertyMap containing initial properties for the control
0082: * @param controlIntf the control interface or extension directly implemented by the control bean
0083: */
0084: protected ControlBean(
0085: org.apache.beehive.controls.api.context.ControlBeanContext context,
0086: String id, PropertyMap initProperties, Class controlIntf) {
0087: super ();
0088:
0089: _localID = id;
0090: _controlIntf = controlIntf;
0091:
0092: //
0093: // If no containing context was specified during construction, see if there is a current
0094: // active container context and implicitly associated the control with it.
0095: //
0096: if (context == null)
0097: context = ControlThreadContext.getContext();
0098:
0099: ControlBeanContextFactory cbcFactory = lookupControlBeanContextFactory(context);
0100: _cbc = cbcFactory.instantiate(this );
0101:
0102: //
0103: // Associate this bean with the context. Beans may run without a context!
0104: // Note that the add() call has the side-effect of calling ControlBean.setBeanContext(), which does
0105: // additional setup work, so we make sure we always call that anyways!
0106: //
0107: if (context != null)
0108: context.add(this );
0109: else
0110: setBeanContext(null);
0111:
0112: //
0113: // Get the default map for the control class. This contains the default property
0114: // values for all beans of the class.
0115: //
0116: PropertyMap classMap = getAnnotationMap(controlIntf);
0117: if (initProperties != null) {
0118: //
0119: // If the initialization map derives its values from a Java annotated element,
0120: // then allow container overrides on this element to be applied. This will also
0121: // coelesce maps referencing the same element.
0122: //
0123: AnnotatedElement annotElem = null;
0124: if (initProperties instanceof AnnotatedElementMap) {
0125: annotElem = ((AnnotatedElementMap) initProperties)
0126: .getAnnotatedElement();
0127: initProperties = getAnnotationMap(annotElem);
0128: }
0129:
0130: //
0131: // If an initial property map was provided, set it to delegate to the default
0132: // map, and then create a wrapper map around it for storing per instance
0133: // properties.
0134: //
0135: if (annotElem != controlIntf)
0136: initProperties.setDelegateMap(classMap);
0137: _properties = new BeanPropertyMap(initProperties);
0138: } else {
0139: //
0140: // If no initial map was provided, simply create an empty map wrapping the
0141: // control class default.
0142: //
0143: _properties = new BeanPropertyMap(classMap);
0144: }
0145:
0146: }
0147:
0148: /**
0149: * Configure this bean to be thread-safe given the threading settings of the impl class and
0150: * the outer container.
0151: */
0152: private void ensureThreadingBehaviour() {
0153: //
0154: // If the implementation class requires a guarantee of single-threaded behavior and the
0155: // outer container does not guarantee it, then enable invocation locking on this
0156: // bean instance.
0157: //
0158: if (hasSingleThreadedImpl() && !_cbc.hasSingleThreadedParent())
0159: _invokeLock = new Semaphore(1, true);
0160: else
0161: _invokeLock = null;
0162: }
0163:
0164: /**
0165: * Return the BeanContextService proxy associated with this bean instance
0166: */
0167: final public ControlBeanContext getBeanContextProxy() {
0168: return _cbc;
0169: }
0170:
0171: /**
0172: * Returns the nesting BeanContext for this ControlBean. This is thread-safe even though it
0173: * is not synchronized.
0174: */
0175: final public BeanContext getBeanContext() {
0176: //
0177: // Indirect through the bean proxy for this control bean and up to its parent nesting
0178: // context. Both these calls (getBeanContextProxy() and getBeanContext()) are, and must
0179: // remain, thread-safe.
0180: //
0181: return getBeanContextProxy().getBeanContext();
0182: }
0183:
0184: /**
0185: * Called by the BeanContextProxy (_cbc) whenever the _parent_ context containing this control bean is
0186: * changed. This is the place to do any initialization (or reinitialization) that is dependent
0187: * upon attributes of the container for the ControlBean.
0188: *
0189: * Note: this is called in the ControlBean ctor, when a parent context calls add() on the nascent
0190: * bean.
0191: *
0192: * @param bc the new parent context containing this control bean (not _cbc)
0193: */
0194: final public synchronized void setBeanContext(BeanContext bc) {
0195: ensureThreadingBehaviour();
0196: }
0197:
0198: /**
0199: * Returns the control ID for this control
0200: */
0201: final public String getControlID() {
0202: return _cbc.getControlID();
0203: }
0204:
0205: /**
0206: * Returns the public interface for this control.
0207: */
0208: final public Class getControlInterface() {
0209: return _controlIntf;
0210: }
0211:
0212: /**
0213: * Returns true if the implementation class for this ControlBean requires the framework
0214: * to ensure thread-safety for it.
0215: */
0216: /*package*/boolean hasSingleThreadedImpl() {
0217: return _threadingPolicy == ThreadingPolicy.SINGLE_THREADED;
0218: }
0219:
0220: /**
0221: * Obtains an instance of the appropriate ImplInitializer class
0222: */
0223: protected synchronized ImplInitializer getImplInitializer() {
0224: if (_implInitializer == null) {
0225: try {
0226: Class initClass = _implClass
0227: .getClassLoader()
0228: .loadClass(_implClass.getName() + "Initializer");
0229: _implInitializer = (ImplInitializer) initClass
0230: .newInstance();
0231: } catch (Exception e) {
0232: throw new ControlException(
0233: "Control initialization failure", e);
0234: }
0235: }
0236: return _implInitializer;
0237: }
0238:
0239: /**
0240: * Returns the target control instance associated with this ControlBean, performing lazy
0241: * instantiation and initialization of the instance.
0242: *
0243: * REVIEW: could probably improve the granularity of locking here, but start w/ just
0244: * synchronizing the entire fn.
0245: */
0246: public synchronized Object ensureControl() {
0247: if (_control == null) {
0248: //
0249: // See if the property map specifies an implementation class for the control;
0250: // if not, use default binding.
0251: //
0252:
0253: String implBinding = null;
0254: BaseProperties bp = _properties
0255: .getPropertySet(BaseProperties.class);
0256: if (bp != null)
0257: implBinding = bp.controlImplementation();
0258: else
0259: implBinding = ControlUtils
0260: .getDefaultControlBinding(_controlIntf);
0261:
0262: try {
0263: _implClass = _controlIntf.getClassLoader().loadClass(
0264: implBinding);
0265:
0266: //
0267: // Validate that the specified implementation class has an @ControlImplementation
0268: // annotation, else downstream requirements (such as having a valid control init
0269: // class) will not be met.
0270: //
0271: if (_implClass
0272: .getAnnotation(ControlImplementation.class) == null) {
0273: throw new ControlException(
0274: "@"
0275: + ControlImplementation.class
0276: .getName()
0277: + " annotation is missing from control implementation class: "
0278: + _implClass.getName());
0279: }
0280: } catch (ClassNotFoundException cnfe) {
0281: throw new ControlException(
0282: "Unable to load control implementation: "
0283: + implBinding, cnfe);
0284: }
0285:
0286: //
0287: // Cache the threading policy associated with the impl
0288: //
0289: Threading thr = (Threading) _implClass
0290: .getAnnotation(Threading.class);
0291: if (thr != null)
0292: _threadingPolicy = thr.value();
0293: else
0294: _threadingPolicy = ThreadingPolicy.SINGLE_THREADED; // default to single-threaded
0295:
0296: ensureThreadingBehaviour();
0297:
0298: try {
0299: //
0300: // Create and initialize the new instance
0301: //
0302: _control = _implClass.newInstance();
0303:
0304: try {
0305: /*
0306: Run the ImplInitializer. This class is code generated based on metadata from a control
0307: implementation. If a Control implementation declares event handlers for the
0308: ControlBeanContext or for the ResourceContext, executing this code generated class
0309: will add the appropriate LifeCycle and / or Resource event listeners.
0310: */
0311: getImplInitializer().initialize(this , _control);
0312: _hasServices = true;
0313: } catch (Exception e) {
0314: throw new ControlException(
0315: "Control initialization failure", e);
0316: }
0317:
0318: //
0319: // Once the control is initialized, then allow the associated context
0320: // to do any initialization.
0321: //
0322: ControlBeanContext cbcs = getBeanContextProxy();
0323:
0324: /*
0325: Implementation note: this call will run the LifeCycleListener(s) that have
0326: been wired-up to the ControlBeanContext object associated with this ControlBean.
0327: */
0328: cbcs.initializeControl();
0329: } catch (RuntimeException re) {
0330: // never mask RuntimeExceptions
0331: throw re;
0332: } catch (Exception e) {
0333: throw new ControlException(
0334: "Unable to create control instance", e);
0335: }
0336: }
0337:
0338: //
0339: // If the implementation instance does not currently have contextual services, they
0340: // are lazily restored here.
0341: //
0342: if (!_hasServices) {
0343: getImplInitializer().initServices(this , _control);
0344: _hasServices = true;
0345: }
0346:
0347: return _control;
0348: }
0349:
0350: /**
0351: * Returns the implementation instance associated with this ControlBean.
0352: */
0353: /* package */Object getImplementation() {
0354: return _control;
0355: }
0356:
0357: /**
0358: * The preinvoke method is called before all operations on the control. In addition to
0359: * providing a basic hook for logging, context initialization, resource management,
0360: * and other common services, it also provides a hook for interceptors.
0361: */
0362: protected void preInvoke(Method m, Object[] args,
0363: String[] interceptorNames) throws InterceptorPivotException {
0364: //
0365: // If the implementation expects single threaded behavior and our container does
0366: // not guarantee it, then enforce it locally here
0367: //
0368: if (_invokeLock != null) {
0369: try {
0370: _invokeLock.acquire();
0371: } catch (InterruptedException ie) {
0372: }
0373: }
0374:
0375: //
0376: // Process interceptors
0377: //
0378: if (interceptorNames != null) {
0379: for (String n : interceptorNames) {
0380: Interceptor i = ensureInterceptor(n);
0381: try {
0382: i.preInvoke(this , m, args);
0383: } catch (InterceptorPivotException ipe) {
0384: ipe.setInterceptorName(n);
0385: throw ipe;
0386: }
0387: }
0388: }
0389:
0390: Vector<InvokeListener> invokeListeners = getInvokeListeners();
0391: if (invokeListeners.size() > 0) {
0392: for (InvokeListener listener : invokeListeners)
0393: listener.preInvoke(m, args);
0394: }
0395: }
0396:
0397: /**
0398: * The preinvoke method is called before all operations on the control. It is the basic
0399: * hook for logging, context initialization, resource management, and other common
0400: * services
0401: */
0402: protected void preInvoke(Method m, Object[] args) {
0403: try {
0404: preInvoke(m, args, null);
0405: } catch (InterceptorPivotException ipe) {
0406: //this will never happen because no interceptor is passed.
0407: }
0408: }
0409:
0410: /**
0411: * The postInvoke method is called after all operations on the control. In addition to
0412: * providing the basic hook for logging, context initialization, resource management, and other common
0413: * services, it also provides a hook for interceptors. During preInvoke, interceptors will be
0414: * called in the order that they are in the list. During postInvoke, they will be called in the
0415: * reverse order. Here is an example of the call sequence with I1, I2, and I3 being interceptors in the list:
0416: *
0417: * I1.preInvoke() -> I2.preInvoke() -> I3.preInvoke() -> invoke method
0418: * |
0419: * I1.postInvoke() <- I2.postInvoke() <- I3.postInvoke() <---
0420: *
0421: * In the event that an interceptor in the list pivoted during preInvoke, the "pivotedInterceptor"
0422: * parameter indicates the interceptor that had pivoted, and the interceptors succeeding it in the list will
0423: * not be called during postInvoke.
0424: */
0425: protected void postInvoke(Method m, Object[] args, Object retval,
0426: Throwable t, String[] interceptorNames,
0427: String pivotedInterceptor) {
0428: try {
0429: Vector<InvokeListener> invokeListeners = getInvokeListeners();
0430: if (invokeListeners.size() > 0) {
0431: for (InvokeListener listener : invokeListeners)
0432: listener.postInvoke(retval, t);
0433: }
0434:
0435: //
0436: // Process interceptors
0437: //
0438: if (interceptorNames != null) {
0439: for (int cnt = interceptorNames.length - 1; cnt >= 0; cnt--) {
0440: String n = interceptorNames[cnt];
0441: if (pivotedInterceptor == null
0442: || n.equals(pivotedInterceptor)) {
0443: pivotedInterceptor = null;
0444: Interceptor i = ensureInterceptor(n);
0445: i.postInvoke(this , m, args, retval, t);
0446: }
0447: }
0448: }
0449: } finally {
0450: //
0451: // Release any lock obtained above in preInvoke
0452: //
0453: if (_invokeLock != null)
0454: _invokeLock.release();
0455: }
0456: }
0457:
0458: /**
0459: * The postInvoke method is called after all operations on the control. It is the basic
0460: * hook for logging, context initialization, resource management, and other common
0461: * services.
0462: */
0463: protected void postInvoke(Method m, Object[] args, Object retval,
0464: Throwable t) {
0465: postInvoke(m, args, retval, t, null, null);
0466: }
0467:
0468: /**
0469: * Sets the EventNotifier for this ControlBean
0470: */
0471: protected <T> void setEventNotifier(Class<T> eventSet, T notifier) {
0472: _notifiers.put(eventSet, notifier);
0473:
0474: //
0475: // Register this notifier for all EventSet interfaces up the interface inheritance
0476: // hiearachy as well
0477: //
0478: List<Class> super EventSets = new ArrayList<Class>();
0479: getSuperEventSets(eventSet, super EventSets);
0480: Iterator<Class> i = super EventSets.iterator();
0481: while (i.hasNext()) {
0482: Class super EventSet = i.next();
0483: _notifiers.put(super EventSet, notifier);
0484: }
0485: }
0486:
0487: /**
0488: * Finds all of the EventSets extended by the input EventSet, and adds them to
0489: * the provided list.
0490: * @param eventSet
0491: * @param superEventSets
0492: */
0493: private void getSuperEventSets(Class eventSet,
0494: List<Class> super EventSets) {
0495: Class[] super Interfaces = eventSet.getInterfaces();
0496: if (super Interfaces != null) {
0497: for (int i = 0; i < super Interfaces.length; i++) {
0498: Class super Interface = super Interfaces[i];
0499: if (super Interface.isAnnotationPresent(EventSet.class)) {
0500: super EventSets.add(super Interface);
0501:
0502: // Continue traversing up the EventSet inheritance hierarchy
0503: getSuperEventSets(super Interface, super EventSets);
0504: }
0505: }
0506: }
0507: }
0508:
0509: /**
0510: * Returns an EventNotifier/UnicastEventNotifier for this ControlBean for the target event set
0511: */
0512: protected <T> T getEventNotifier(Class<T> eventSet) {
0513: return (T) _notifiers.get(eventSet);
0514: }
0515:
0516: /**
0517: * Returns the list of InvokeListeners for this ControlBean
0518: */
0519: /* package */Vector<InvokeListener> getInvokeListeners() {
0520: if (_invokeListeners == null)
0521: _invokeListeners = new Vector<InvokeListener>();
0522: return _invokeListeners;
0523: }
0524:
0525: /**
0526: * Registers a new InvokeListener for this ControlBean.
0527: */
0528: /* package */void addInvokeListener(InvokeListener invokeListener) {
0529: getInvokeListeners().addElement(invokeListener);
0530: }
0531:
0532: /**
0533: * Deregisters an existing InvokeListener for this ControlBean.
0534: */
0535: /* package */void removeInvokeListener(
0536: InvokeListener invokeListener) {
0537: getInvokeListeners().removeElement(invokeListener);
0538: }
0539:
0540: /**
0541: * Returns the local (parent-relative) ID for this ControlBean
0542: */
0543: protected String getLocalID() {
0544: return _localID;
0545: }
0546:
0547: /**
0548: * Set the local (parent-relative) ID for this ControlBean. It has package access because
0549: * the local ID should only be set from within the associated context, and only when the
0550: * bean is currently anonymous (hence the assertion below)
0551: */
0552: /* package */void setLocalID(String localID) {
0553: assert _localID == null; // should only set if not already set!
0554: _localID = localID;
0555: }
0556:
0557: /**
0558: * Returns the bean context instance associated with the this bean, as opposed to the
0559: * parent context returned by the public getBeanContext() API.
0560: */
0561: public ControlBeanContext getControlBeanContext() {
0562: //
0563: // The peer context instance is the context provider for this ControlBean
0564: //
0565: return getBeanContextProxy();
0566: }
0567:
0568: /**
0569: * Locates and obtains a context service from the BeanContextServices instance
0570: * supporting this bean.
0571: *
0572: * The base design for the BeanContextServicesSupport is that it will delegate up to peers
0573: * in a nesting context, so a nested control bean will look 'up' to find a service provider.
0574: */
0575: protected Object getControlService(Class serviceClass,
0576: Object selector) throws TooManyListenersException
0577:
0578: {
0579: //
0580: // Get the associated context object, then use it to locate the (parent) bean context.
0581: // Services are always provided by the parent context.
0582: //
0583: ControlBeanContext cbc = getControlBeanContext();
0584: BeanContext bc = cbc.getBeanContext();
0585: if (bc == null || !(bc instanceof BeanContextServices))
0586: throw new ControlException("Can't locate service context: "
0587: + bc);
0588:
0589: //
0590: // Call getService on the parent context, using this bean as the requestor and the
0591: // associated peer context instance as the child and event listener parameters.
0592: //
0593: return ((BeanContextServices) bc).getService(cbc, this ,
0594: serviceClass, selector, cbc);
0595: }
0596:
0597: /**
0598: * Sets a property on the ControlBean instance. All generated property setter methods
0599: * will delegate down to this method.
0600: */
0601: protected void setControlProperty(PropertyKey key, Object o) {
0602: AnnotationConstraintValidator.validate(key, o);
0603: _properties.setProperty(key, o);
0604: }
0605:
0606: /**
0607: * Dispatches the requested operation event on the ControlBean.
0608: * @see org.apache.beehive.controls.runtime.bean.ControlContainerContext#dispatchEvent
0609: */
0610: /* package */Object dispatchEvent(EventRef event, Object[] args)
0611: throws IllegalAccessException, IllegalArgumentException,
0612: InvocationTargetException {
0613: ensureControl();
0614:
0615: //
0616: // Translate the EventRef back to an actual event method on the ControlInterface
0617: //
0618: Class controlInterface = getControlInterface();
0619: Method method = event.getEventMethod(controlInterface);
0620:
0621: //
0622: // Locate the target of the event
0623: //
0624: Object eventTarget = null;
0625: if (method.getDeclaringClass().isAssignableFrom(
0626: _control.getClass())) {
0627: //
0628: // If the control implementation implements that EventSet interface, then
0629: // dispatch the event directly to it, and allow it do make the decision about
0630: // how/when to dispatch to any external listeners (via a @Client notifier
0631: // instance)
0632: //
0633: eventTarget = _control;
0634: } else {
0635: //
0636: // The associated control implementation does not directly handle the event,
0637: // so find the event notifier instance for the EventSet interface associated
0638: // with the method.
0639: //
0640: eventTarget = _notifiers.get(method.getDeclaringClass());
0641: if (eventTarget == null)
0642: throw new IllegalArgumentException(
0643: "No event notifier found for " + event);
0644: }
0645:
0646: //
0647: // Dispatch the event
0648: //
0649: return method.invoke(eventTarget, args);
0650: }
0651:
0652: /**
0653: * Returns a property on the ControlBean instance. This version does not coerce
0654: * an annotation type property from a PropertyMap to a proxy instance of the
0655: * type.
0656: */
0657: protected Object getRawControlProperty(PropertyKey key) {
0658: return _properties.getProperty(key);
0659: }
0660:
0661: /**
0662: * Returns a property on the ControlBean instance. All generated property getter methods
0663: * will delegate down to this method
0664: */
0665: protected Object getControlProperty(PropertyKey key) {
0666: Object value = getRawControlProperty(key);
0667:
0668: // If the held value is a PropertyMap, then wrap it in an annotation proxy of
0669: // the expected type.
0670: if (value instanceof PropertyMap) {
0671: PropertyMap map = (PropertyMap) value;
0672: value = PropertySetProxy.getProxy(map.getMapClass(), map);
0673: }
0674:
0675: return value;
0676: }
0677:
0678: /* this method is implemented during code generation by a ControlBean extension */
0679: /**
0680: * Returns the local cache for ControlBean property maps.
0681: */
0682: abstract protected Map getPropertyMapCache();
0683:
0684: /**
0685: * Returns the PropertyMap containing values associated with an AnnotatedElement. Elements
0686: * that are associated with the bean's Control interface will be locally cached.
0687: */
0688: protected PropertyMap getAnnotationMap(AnnotatedElement annotElem) {
0689: Map annotCache = getPropertyMapCache();
0690:
0691: // If in the cache already , just return it
0692: if (annotCache.containsKey(annotElem))
0693: return (PropertyMap) annotCache.get(annotElem);
0694:
0695: //
0696: // Ask the associated ControlBeanContext to locate and initialize a PropertyMap, then
0697: // store it in the local cache.
0698: //
0699: PropertyMap map = getControlBeanContext().getAnnotationMap(
0700: annotElem);
0701: annotCache.put(annotElem, map);
0702:
0703: return map;
0704: }
0705:
0706: /**
0707: * Returns the property map containing the properties for the bean
0708: */
0709: /* package */BeanPropertyMap getPropertyMap() {
0710: return _properties;
0711: }
0712:
0713: /**
0714: * This protected version is only available to concrete subclasses that expose bound
0715: * property support. This method is synchronized to enable lazy instantiation, in
0716: * the belief that it is a bigger win to avoid allocating when there are no listeners
0717: * than it is to introduce synchronization overhead on access.
0718: */
0719: synchronized protected PropertyChangeSupport getPropertyChangeSupport() {
0720: if (_changeSupport == null)
0721: _changeSupport = new PropertyChangeSupport(this );
0722:
0723: return _changeSupport;
0724: }
0725:
0726: /**
0727: * Delivers a PropertyChangeEvent to any registered PropertyChangeListeners associated
0728: * with the property referenced by the specified key.
0729: *
0730: * This method *should not* be synchronized, as the PropertyChangeSupport has its own
0731: * built in synchronization mechanisms.
0732: */
0733: protected void firePropertyChange(PropertyKey propertyKey,
0734: Object oldValue, Object newValue) {
0735: // No change support instance means no listeners
0736: if (_changeSupport == null)
0737: return;
0738:
0739: _changeSupport.firePropertyChange(
0740: propertyKey.getPropertyName(), oldValue, newValue);
0741: }
0742:
0743: /**
0744: * This protected version is only available to concrete subclasses that expose bound
0745: * property support. This method is synchronized to enable lazy instantiation, in
0746: * the belief that is a bigger win to avoid allocating when there are no listeners
0747: * than it is to introduce synchronization overhead on access.
0748: */
0749: synchronized protected VetoableChangeSupport getVetoableChangeSupport() {
0750: if (_vetoSupport == null)
0751: _vetoSupport = new VetoableChangeSupport(this );
0752:
0753: return _vetoSupport;
0754: }
0755:
0756: /**
0757: * Delivers a PropertyChangeEvent to any registered VetoableChangeListeners associated
0758: * with the property referenced by the specified key.
0759: *
0760: * This method *should not* be synchronized, as the VetoableChangeSupport has its own
0761: * built in synchronization mechanisms.
0762: */
0763: protected void fireVetoableChange(PropertyKey propertyKey,
0764: Object oldValue, Object newValue)
0765: throws java.beans.PropertyVetoException {
0766: // No veto support instance means no listeners
0767: if (_vetoSupport == null)
0768: return;
0769:
0770: _vetoSupport.fireVetoableChange(propertyKey.getPropertyName(),
0771: oldValue, newValue);
0772: }
0773:
0774: /**
0775: * Returns the parameter names for a method on the ControlBean. Actual mapping is done
0776: * by generated subclasses, so if we reach the base ControlBean implementation, then
0777: * no parameter names are available for the target method.
0778: */
0779: protected String[] getParameterNames(Method m) {
0780: throw new IllegalArgumentException(
0781: "No parameter name data for " + m);
0782: }
0783:
0784: /**
0785: * Computes the most derived ControlInterface for the specified ControlExtension.
0786: * @param controlIntf
0787: * @return the most derived ControlInterface
0788: * @deprecated Use {@link ControlUtils#getMostDerivedInterface(Class)} instead. This method will
0789: * be removed in the next release.
0790: */
0791: public static Class getMostDerivedInterface(Class controlIntf) {
0792: return ControlUtils.getMostDerivedInterface(controlIntf);
0793: }
0794:
0795: /**
0796: * Enforces the VersionRequired annotation at runtime (called from each ControlBean).
0797: * @param intfName
0798: * @param version
0799: * @param versionRequired
0800: */
0801: protected static void enforceVersionRequired(String intfName,
0802: Version version, VersionRequired versionRequired) {
0803: if (versionRequired != null) {
0804: int majorRequired = versionRequired.major();
0805: int minorRequired = versionRequired.minor();
0806:
0807: if (majorRequired < 0) // no real version requirement
0808: return;
0809:
0810: int majorPresent = -1;
0811: int minorPresent = -1;
0812: if (version != null) {
0813: majorPresent = version.major();
0814: minorPresent = version.minor();
0815:
0816: if (majorRequired <= majorPresent
0817: && (minorRequired < 0 || minorRequired <= minorPresent)) {
0818: // Version requirement is satisfied
0819: return;
0820: }
0821: }
0822:
0823: //
0824: // Version requirement failed
0825: //
0826: throw new ControlException(
0827: "Control extension "
0828: + intfName
0829: + " fails version requirement: requires interface version "
0830: + majorRequired + "." + minorRequired
0831: + ", found interface version "
0832: + majorPresent + "." + minorPresent + ".");
0833: }
0834: }
0835:
0836: /**
0837: * Implementation of the Java serialization writeObject method
0838: */
0839: private synchronized void writeObject(ObjectOutputStream oos)
0840: throws IOException {
0841: if (_control != null) {
0842: //
0843: // If the implementation class is marked as transient/stateless, then reset the
0844: // reference to it prior to serialization. A new instance will be created by
0845: // ensureControl() upon first use after deserialization.
0846: // If the implementation class is not transient, then invoke the ImplInitializer
0847: // resetServices method to reset all contextual service references to null, as
0848: // contextual services should never be serializated and always reassociated on
0849: // deserialization.
0850: //
0851: ControlImplementation implAnnot = (ControlImplementation) _implClass
0852: .getAnnotation(ControlImplementation.class);
0853: assert implAnnot != null;
0854: if (implAnnot.isTransient()) {
0855: _control = null;
0856: } else {
0857: getImplInitializer().resetServices(this , _control);
0858: _hasServices = false;
0859: }
0860: }
0861:
0862: oos.defaultWriteObject();
0863: }
0864:
0865: /**
0866: * Called during XMLDecoder reconstruction of a ControlBean.
0867: */
0868: public void decodeImpl(Object impl) {
0869: if (impl != _control)
0870: throw new ControlException("Cannot change implementation");
0871: }
0872:
0873: /**
0874: * Internal method used to lookup a ControlBeanContextFactory. This factory is used to create the
0875: * ControlBeanContext object for this ControlBean. The factory is discoverable from either the containing
0876: * ControlBeanContext object or from the environment. If the containing CBC object exposes a
0877: * contextual service of type {@link ControlBeanContextFactory}, the factory returned from this will
0878: * be used to create a ControlBeanContext object.
0879: *
0880: * @param context
0881: * @return the ControlBeanContextFactory discovered in the environment or a default one if no factory is configured
0882: */
0883: private ControlBeanContextFactory lookupControlBeanContextFactory(
0884: org.apache.beehive.controls.api.context.ControlBeanContext context) {
0885:
0886: // first, try to find the CBCFactory from the container
0887: if (context != null) {
0888: ControlBeanContextFactory cbcFactory = context.getService(
0889: ControlBeanContextFactory.class, null);
0890:
0891: if (cbcFactory != null) {
0892: return cbcFactory;
0893: }
0894: }
0895:
0896: // Create the context that acts as the BeanContextProxy for this bean (the context that this bean _defines_).
0897: try {
0898: DiscoverClass discoverer = new DiscoverClass();
0899: Class factoryClass = discoverer.find(
0900: ControlBeanContextFactory.class,
0901: DefaultControlBeanContextFactory.class.getName());
0902:
0903: return (ControlBeanContextFactory) factoryClass
0904: .newInstance();
0905: } catch (Exception e) {
0906: throw new ControlException(
0907: "Exception creating ControlBeanContext", e);
0908: }
0909: }
0910:
0911: /**
0912: * Retrieves interceptor instances, creates them lazily.
0913: */
0914: protected Interceptor ensureInterceptor(String n) {
0915: Interceptor i = null;
0916: if (_interceptors == null) {
0917: _interceptors = new HashMap<String, Interceptor>();
0918: } else {
0919: i = _interceptors.get(n);
0920: }
0921:
0922: if (i == null) {
0923: try {
0924: i = (Interceptor) getControlService(
0925: getControlBeanContext().getClassLoader()
0926: .loadClass(n), null);
0927: } catch (Exception e) {
0928: // Couldn't instantiate the desired service; usually this is because the service interface itself
0929: // isn't present on this system at runtime (ClassNotFoundException), or if the container of the
0930: // control didn't registers the service.
0931:
0932: /* TODO log a message here to that effect, but just swallow the exception for now. */
0933: } finally {
0934: // We want to always return an interceptor, so if we can't get the one we want, we'll substitute
0935: // a "null" interceptor that does nothing.
0936: if (i == null)
0937: i = new NullInterceptor();
0938:
0939: _interceptors.put(n, i);
0940: }
0941: }
0942: return i;
0943: }
0944:
0945: /**
0946: * The "null" interceptor that does nothing. Used when a specific interceptor
0947: * is unavailable at runtime.
0948: */
0949: static private class NullInterceptor implements Interceptor {
0950: public void preInvoke(
0951: org.apache.beehive.controls.api.bean.ControlBean cb,
0952: Method m, Object[] args) {
0953: }
0954:
0955: public void postInvoke(
0956: org.apache.beehive.controls.api.bean.ControlBean cb,
0957: Method m, Object[] args, Object retval, Throwable t) {
0958: }
0959:
0960: public void preEvent(
0961: org.apache.beehive.controls.api.bean.ControlBean cb,
0962: Class eventSet, Method m, Object[] args) {
0963: }
0964:
0965: public void postEvent(
0966: org.apache.beehive.controls.api.bean.ControlBean cb,
0967: Class eventSet, Method m, Object[] args, Object retval,
0968: Throwable t) {
0969: }
0970: }
0971:
0972: /** BEGIN unsynchronized fields */
0973:
0974: /**
0975: * The following fields are initialized in the constructor and never subsequently changed,
0976: * so they are safe for unsynchronized read access
0977: */
0978:
0979: /**
0980: * The control implementation class bound to this ControlBean
0981: */
0982: protected Class _implClass;
0983:
0984: /**
0985: * The threading policy associated with the control implementation wrapped by this
0986: * ControlBean. Initialized to MULTI_THREADED in order to assume multi-threadedness
0987: * until a bean is associated with a specific (potentially single-threaded) implementation.
0988: */
0989: transient private ThreadingPolicy _threadingPolicy = ThreadingPolicy.MULTI_THREADED;
0990:
0991: /**
0992: * Contains the per-instance properties set for this ControlBean.
0993: */
0994: private BeanPropertyMap _properties;
0995:
0996: /** END unsynchronized fields */
0997:
0998: /* BEGIN synchronized fields */
0999:
1000: /*
1001: * The following fields must be:
1002: * 1) only written in synchronized methods or (unsynchronized) constructors
1003: * 2) only read in synchronized methods or methods that are safe wrt the values changing during
1004: * execution.
1005: */
1006:
1007: /**
1008: * The control implementation instance wrapped by this ControlBean
1009: */
1010: private Object _control;
1011:
1012: /**
1013: * The control bean context instance associated with this ControlBean
1014: */
1015: private ControlBeanContext _cbc;
1016:
1017: /**
1018: * An ImplInitializer instances used to initialize/reset the state of the associated
1019: * implementation instance.
1020: */
1021: transient private ImplInitializer _implInitializer;
1022:
1023: /**
1024: * Indicates whether the contextual services associated with the bean have been
1025: * fully initialized.
1026: */
1027: transient private boolean _hasServices = false;
1028:
1029: /**
1030: * Used to guarantee single threaded invocation when required. If the
1031: * outer container provides the guarantee or the implementation itself
1032: * is threadsafe, then the value will be null.
1033: */
1034: transient private Semaphore _invokeLock;
1035:
1036: /**
1037: * This field manages PropertyChangeListeners (if supporting bound properties).
1038: */
1039: private PropertyChangeSupport _changeSupport;
1040:
1041: /**
1042: * This field manages VetoabbleChangeListeners (if supporting constrained properties)
1043: */
1044: private VetoableChangeSupport _vetoSupport;
1045:
1046: /** END synchronized fields */
1047:
1048: /**
1049: * The (context relative) control ID associated with this instance
1050: */
1051: private String _localID;
1052:
1053: /**
1054: * The public control interface associated with this ControlBean
1055: */
1056: private Class _controlIntf;
1057:
1058: /**
1059: * This field manages the register listener list(s) associated with event set interfaces
1060: * for the ControlBean. The value objects are either UnicastEventNotifier or EventNotifier
1061: * instances, depending upon whether the associated EventSet interface is unicast or
1062: * multicast.
1063: */
1064: private HashMap<Class, Object> _notifiers = new HashMap<Class, Object>();
1065:
1066: /**
1067: * Maintains the list of callback event listeners (if any) for this ControlBean.
1068: */
1069: transient private Vector<InvokeListener> _invokeListeners;
1070:
1071: /**
1072: * HashMap to hold interceptor impl instances.
1073: * Populated lazily. Maps interceptor interface name to impl.
1074: */
1075: transient private HashMap<String, Interceptor> _interceptors;
1076: }
|