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.PropertyChangeEvent;
0022: import java.beans.PropertyVetoException;
0023: import java.beans.PropertyChangeListener;
0024: import java.beans.VetoableChangeListener;
0025: import java.beans.beancontext.BeanContext;
0026: import java.beans.beancontext.BeanContextServiceRevokedEvent;
0027: import java.beans.beancontext.BeanContextServiceProvider;
0028: import java.beans.beancontext.BeanContextServices;
0029: import java.beans.beancontext.BeanContextChild;
0030: import java.beans.beancontext.BeanContextServiceRevokedListener;
0031: import java.beans.beancontext.BeanContextServicesListener;
0032: import java.beans.beancontext.BeanContextMembershipListener;
0033: import java.beans.beancontext.BeanContextServiceAvailableEvent;
0034: import java.lang.annotation.Annotation;
0035: import java.lang.reflect.AnnotatedElement;
0036: import java.lang.reflect.Field;
0037: import java.lang.reflect.Method;
0038: import java.util.Collections;
0039: import java.util.HashMap;
0040: import java.util.HashSet;
0041: import java.util.Iterator;
0042: import java.util.Map;
0043: import java.util.Set;
0044: import java.util.TooManyListenersException;
0045: import java.util.Vector;
0046: import java.util.Collection;
0047: import java.io.IOException;
0048: import java.io.InputStream;
0049: import java.io.ObjectOutputStream;
0050: import java.io.ObjectInputStream;
0051: import java.net.URL;
0052:
0053: import org.apache.beehive.controls.api.ControlException;
0054: import org.apache.beehive.controls.api.context.ControlHandle;
0055: import org.apache.beehive.controls.api.properties.AnnotatedElementMap;
0056: import org.apache.beehive.controls.api.properties.BeanPropertyMap;
0057: import org.apache.beehive.controls.api.properties.PropertyMap;
0058: import org.apache.beehive.controls.api.properties.PropertySet;
0059: import org.apache.beehive.controls.api.properties.PropertySetProxy;
0060:
0061: /**
0062: * The ControlBeanContext implements the basic BeanContextServices implementation
0063: * for ControlBeans.
0064: *
0065: * It provides several basic functions:
0066: * - it defines the generic services that are available for all control containers
0067: * - it acts as the base class for other container service implementations
0068: * - it acts as the BeanContextServicesRevokedListener when an associated control
0069: * bean has lost access to services.
0070: */
0071: public class ControlBeanContext implements
0072: org.apache.beehive.controls.api.context.ControlBeanContext,
0073: java.beans.PropertyChangeListener,
0074: java.beans.VetoableChangeListener, java.io.Serializable {
0075: /**
0076: * Creates a new ControlBeanContext instance associated with a specific control bean. If the
0077: * <code>ControlBean</code> is null, this ControlBeanContext object represents a top-level Control
0078: * container. This constructor uses the default implementation of the
0079: * {@link java.beans.beancontext.BeanContextServices} interface from the JDK.
0080: *
0081: * @param bean The control bean that contains/scopes the ControlBeanContext. If null, it means the
0082: * ControlBeanContext is for a top-level container.
0083: */
0084: protected ControlBeanContext(ControlBean bean) {
0085: this (bean, DEFAULT_BEAN_CONTEXT_SERVICES_FACTORY);
0086: }
0087:
0088: /**
0089: * Creates a new ControlBeanContext instance associated with a specific control bean. If the
0090: * <code>ControlBean</code> is null, this ControlBeanContext object represents a top-level Control
0091: * container. This constructor uses the <code>beanContextServicesDelegate</code> instance as the
0092: * implementation of the {@link java.beans.beancontext.BeanContextServices} interface.
0093: *
0094: * @param bean The control bean
0095: * @param beanContextServicesFactory A factory that can be used to create the BeanContextServicesFactory object
0096: * that implements support for the {@link BeanContextServices} interface.
0097: */
0098: protected ControlBeanContext(ControlBean bean,
0099: BeanContextServicesFactory beanContextServicesFactory) {
0100: super ();
0101:
0102: _bean = bean;
0103:
0104: // ensure that there is a valid factory for creating the BCS delegate
0105: if (beanContextServicesFactory == null)
0106: beanContextServicesFactory = DEFAULT_BEAN_CONTEXT_SERVICES_FACTORY;
0107:
0108: _beanContextServicesDelegate = beanContextServicesFactory
0109: .instantiate(this );
0110: initialize();
0111: }
0112:
0113: /**
0114: * Called by BeanContextSupport superclass during construction and deserialization to
0115: * initialize subclass transient state
0116: */
0117: public void initialize() {
0118: //
0119: // Register the ControlBeanContext provider on all new context instances.
0120: //
0121: addService(
0122: org.apache.beehive.controls.api.context.ControlBeanContext.class,
0123: CONTROL_BEAN_CONTEXT_PROVIDER);
0124: }
0125:
0126: /**
0127: * Implements the
0128: * {@link java.beans.beancontext.BeanContextServiceRevokedListener#serviceRevoked(java.beans.beancontext.BeanContextServiceRevokedEvent)}
0129: * method. This is called if the associated {@link ControlBean} has requested a service that is being subsequently
0130: * revoked.
0131: */
0132: public void serviceRevoked(BeanContextServiceRevokedEvent bcsre) {
0133: //
0134: // This can happen, if the control is disassociated from a parent context that is
0135: // providing services.
0136: //
0137: }
0138:
0139: /**
0140: * Overrides the {@link java.beans.beancontext.BeanContextChild#setBeanContext(java.beans.beancontext.BeanContext)}
0141: * method. This hook is used to perform additional processing that needs to occur when the control is associated
0142: * with a new nesting context.
0143: */
0144: public synchronized void setBeanContext(BeanContext beanContext)
0145: throws PropertyVetoException {
0146: ControlBeanContext cbcs = null;
0147:
0148: if (beanContext != null) {
0149: //
0150: // ControlBeans can only be nested in context service instances that derive
0151: // from ControlBeanContext.
0152: //
0153: if (!(beanContext instanceof ControlBeanContext)) {
0154: PropertyChangeEvent pce = new PropertyChangeEvent(
0155: _bean, "beanContext", getBeanContext(),
0156: beanContext);
0157: throw new PropertyVetoException(
0158: "Context does not support nesting controls: "
0159: + beanContext.getClass(), pce);
0160: }
0161:
0162: cbcs = (ControlBeanContext) beanContext;
0163: }
0164:
0165: _beanContextServicesDelegate.setBeanContext(beanContext);
0166:
0167: resetControlID();
0168:
0169: _hasSingleThreadedParent = cbcs != null ? cbcs
0170: .isSingleThreadedContainer() : false;
0171:
0172: //
0173: // Notify the bean that its context (container) has been set.
0174: //
0175: if (_bean != null)
0176: _bean.setBeanContext(beanContext);
0177: }
0178:
0179: /**
0180: * The NameGenerator class is a simple helper class that creates new unique names based
0181: * upon a base prefix and an incrementing counter
0182: */
0183: private static class NameGenerator implements java.io.Serializable {
0184: NameGenerator(String namePrefix) {
0185: _namePrefix = namePrefix;
0186: }
0187:
0188: /**
0189: * Get the next unique name
0190: */
0191: public synchronized String next() {
0192: return _namePrefix + _nextIndex++;
0193: }
0194:
0195: int _nextIndex = 0;
0196: String _namePrefix;
0197: }
0198:
0199: /**
0200: * Returns a new NameGenerator instance based upon a particular naming
0201: * prefix.
0202: */
0203: private NameGenerator getNameGenerator(String namePrefix) {
0204: synchronized (this ) {
0205: if (_nameGenerators == null)
0206: _nameGenerators = new HashMap<String, NameGenerator>();
0207:
0208: NameGenerator nameGenerator = _nameGenerators
0209: .get(namePrefix);
0210: if (nameGenerator == null) {
0211: nameGenerator = new NameGenerator(namePrefix);
0212: _nameGenerators.put(namePrefix, nameGenerator);
0213: }
0214: return nameGenerator;
0215: }
0216: }
0217:
0218: /**
0219: * Generates a new unique control ID for an instance of the target class
0220: */
0221: public String generateUniqueID(Class clazz) {
0222: String namePrefix = clazz.getName();
0223: int dotIndex = namePrefix.lastIndexOf('.');
0224: if (dotIndex > 0)
0225: namePrefix = namePrefix.substring(dotIndex + 1);
0226: NameGenerator nameGenerator = getNameGenerator(namePrefix);
0227: return nameGenerator.next();
0228: }
0229:
0230: /**
0231: * Overrides the BeanContextSupport.add() method to perform additional validation
0232: * that is unique for ControlBeans containers.
0233: */
0234: public boolean add(Object targetChild) {
0235: //
0236: // The context can contain ControlBeans and other types of objects, such as a control
0237: // factory.
0238: //
0239: String beanID = null;
0240: if (targetChild instanceof ControlBean) {
0241: ControlBean bean = (ControlBean) targetChild;
0242: beanID = bean.getLocalID();
0243:
0244: //
0245: // The bean is anonymous, so we must generate a new unique name within this context.
0246: //
0247: if (beanID == null) {
0248: beanID = generateUniqueID(bean.getClass());
0249: bean.setLocalID(beanID);
0250: }
0251:
0252: ControlBean existingBean = (ControlBean) _childMap
0253: .get(beanID);
0254: if (existingBean != null && existingBean != targetChild) {
0255: throw new IllegalArgumentException(
0256: "Attempting to add control with duplicate ID: "
0257: + beanID);
0258: }
0259: }
0260:
0261: boolean added = _beanContextServicesDelegate.add(targetChild);
0262: if (added && beanID != null)
0263: _childMap.put(beanID, targetChild);
0264:
0265: return added;
0266: }
0267:
0268: /**
0269: * Overrides the BeanContextSupport.remove() method to perform additional post-processing
0270: * on child removal.
0271: */
0272: public boolean remove(Object targetChild) {
0273: assert targetChild instanceof ControlBean; // should be guaranteed above
0274: boolean removed = _beanContextServicesDelegate
0275: .remove(targetChild);
0276:
0277: if (removed) {
0278: //
0279: // Remove from the locally maintained child map
0280: //
0281: String localID = ((ControlBean) targetChild).getLocalID();
0282: Object removedChild = _childMap.remove(localID);
0283: assert removedChild == targetChild; // just being safe
0284: }
0285: return removed;
0286: }
0287:
0288: /**
0289: * Returns a ControlBean instance nested the current BeanContext.
0290: * @param id the identifier for the target control, relative to the current
0291: * context.
0292: */
0293: public ControlBean getBean(String id) {
0294: // If no control id separator found, the bean is a direct child of this context
0295: int delim = id
0296: .indexOf(org.apache.beehive.controls.api.bean.ControlBean.IDSeparator);
0297: if (delim < 0) // child is a direct descendent
0298: return (ControlBean) _childMap.get(id);
0299:
0300: // Find the child referenced by the first element in the path
0301: ControlBean bean = (ControlBean) _childMap.get(id.substring(0,
0302: delim));
0303: if (bean == null)
0304: return bean;
0305:
0306: // Get the BeanContext associated with the found child, and then ask it
0307: // to resolve the rest of the path
0308: return bean.getBeanContextProxy().getBean(
0309: id.substring(delim + 1));
0310: }
0311:
0312: /**
0313: * Returns the ControlBean associated directly with the ControlBeanContext. If the
0314: * context represents a top-level container, will return null.
0315: */
0316: public ControlBean getControlBean() {
0317: return _bean;
0318: }
0319:
0320: public synchronized boolean hasSingleThreadedParent() {
0321: return _hasSingleThreadedParent;
0322: }
0323:
0324: /**
0325: * Returns true if this container associated with this context service enforces
0326: * single-threaded invocation, false otherwise.
0327: *
0328: * This MUST be overridden by container-specific subclasses in order to change
0329: * the default behavior. If a single-threaded container intends to guarantee
0330: * single-threaded behavior (such as the EJB container), this should return true.
0331: */
0332: public synchronized boolean isSingleThreadedContainer() {
0333: return (hasSingleThreadedParent() || (_bean != null && _bean
0334: .hasSingleThreadedImpl()));
0335: }
0336:
0337: /**
0338: * The initializeControl method is invoked when the implementation instance associated
0339: * with the context has been instantiated and initialized.
0340: */
0341: public void initializeControl() {
0342: //
0343: // Deliver the onCreate event to any register lifecycle listeners
0344: //
0345: if (_lifeCycleListeners != null) {
0346: for (LifeCycle lifeCycleListener : _lifeCycleListeners) {
0347: lifeCycleListener.onCreate();
0348: }
0349: }
0350: }
0351:
0352: /**
0353: * Returns the PropertyMap containing default properties for an AnnotatedElement
0354: * in the current context. The initialization of PropertyMap binding is always
0355: * done by delegating to a {@link ControlContainerContext}, enabling containers to implement
0356: * property binding mechanisms (such as external config) that may override the values
0357: * contained within the element annotations.
0358: */
0359: public PropertyMap getAnnotationMap(AnnotatedElement annotElem) {
0360: ControlBeanContext beanContext = this ;
0361: while (beanContext != null) {
0362: // REVIEW: should ControlContainerContext-derived classes override getBeanAnnotationMap? Not sure
0363: // that name makes sense, and perhaps it shouldn't take a ControlBean.
0364: if (beanContext instanceof ControlContainerContext)
0365: return beanContext.getBeanAnnotationMap(_bean,
0366: annotElem);
0367: beanContext = (ControlBeanContext) beanContext
0368: .getBeanContext();
0369: }
0370:
0371: // No ControlContainerContext was found, so just use the default implementation
0372: return getBeanAnnotationMap(_bean, annotElem);
0373: }
0374:
0375: /**
0376: * The default implementation of getBeanAnnotationMap. This returns a map based purely
0377: * upon annotation reflection
0378: */
0379: protected PropertyMap getBeanAnnotationMap(ControlBean bean,
0380: AnnotatedElement annotElem) {
0381: PropertyMap map = new AnnotatedElementMap(annotElem);
0382:
0383: // REVIEW: is this the right place to handle the general control client case?
0384: if (bean != null)
0385: setDelegateMap(map, bean, annotElem);
0386:
0387: return map;
0388: }
0389:
0390: static protected void setDelegateMap(PropertyMap map,
0391: ControlBean bean, AnnotatedElement annotElem) {
0392: //
0393: // If building an annotation map for a method or field, we want to delegate back
0394: // to the base control type.
0395: //
0396: Class annotClass = null;
0397: if (annotElem instanceof Field) {
0398: annotClass = ((Field) annotElem).getType();
0399: } else if (annotElem instanceof Method) {
0400: annotClass = bean.getControlInterface();
0401: }
0402:
0403: if (annotClass != null) {
0404: PropertyMap delegateMap = bean.getAnnotationMap(annotClass);
0405: map.setDelegateMap(delegateMap);
0406: }
0407: }
0408:
0409: //
0410: // ControlBeanContext.getControlInterface
0411: //
0412: public Class getControlInterface() {
0413: return _bean.getControlInterface();
0414: }
0415:
0416: //
0417: // ControlBeanContext.getControlPropertySet
0418: //
0419: public <T extends Annotation> T getControlPropertySet(
0420: Class<T> propertySet) {
0421: PropertyMap map = _bean.getPropertyMap();
0422:
0423: //
0424: // Optional properties are not exposed to clients using traditional JavaBean
0425: // setters/getters (because there is way to represent an 'unset' value); for
0426: // these properties, the impl can tell if the PropertySet is unset because
0427: // this method will return null.
0428: //
0429: if (!map.containsPropertySet(propertySet)) {
0430: PropertySet psAnnot = propertySet
0431: .getAnnotation(PropertySet.class);
0432: if (psAnnot.optional())
0433: return null;
0434: }
0435:
0436: //
0437: // Construct a new PropertySet proxy instance that derives its values from
0438: // the bean property map.
0439: //
0440: return PropertySetProxy.getProxy(propertySet, map);
0441: }
0442:
0443: //
0444: // ControlBeanContext.getMethodPropertySet
0445: //
0446: public <T extends Annotation> T getMethodPropertySet(Method m,
0447: Class<T> propertySet) {
0448: PropertyMap map = _bean.getAnnotationMap(m);
0449:
0450: //
0451: // Optional properties are not exposed to clients using traditional JavaBean
0452: // setters/getters (because there is way to represent an 'unset' value); for
0453: // these properties, the impl can tell if the PropertySet is unset because
0454: // this method will return null.
0455: //
0456: if (!map.containsPropertySet(propertySet)) {
0457: PropertySet psAnnot = propertySet
0458: .getAnnotation(PropertySet.class);
0459: if (psAnnot.optional())
0460: return null;
0461: }
0462:
0463: //
0464: // Construct a new PropertySet proxy instance that derives its values from
0465: // the method property map.
0466: //
0467: return PropertySetProxy.getProxy(propertySet, _bean
0468: .getAnnotationMap(m));
0469: }
0470:
0471: //
0472: // ControlBeanContext.getParameterPropertySet
0473: //
0474: public <T extends Annotation> T getParameterPropertySet(Method m,
0475: int i, Class<T> propertySet)
0476: throws IllegalArgumentException, IndexOutOfBoundsException {
0477: if (i >= m.getParameterTypes().length)
0478: throw new IndexOutOfBoundsException(
0479: "Invalid parameter index for method:" + m);
0480:
0481: // todo: Currently, there is no external override mechanism for method parameters
0482: Annotation[] paramAnnots = m.getParameterAnnotations()[i];
0483: for (Annotation paramAnnot : paramAnnots)
0484: if (propertySet.isAssignableFrom(paramAnnot.getClass()))
0485: return (T) paramAnnot;
0486:
0487: return null;
0488: }
0489:
0490: //
0491: // ControlBeanContext.getParameterNames
0492: //
0493: public String[] getParameterNames(Method m)
0494: throws IllegalArgumentException {
0495: return _bean.getParameterNames(m);
0496: }
0497:
0498: //
0499: // ControlBeanContext.getNamedParameterValue
0500: //
0501: public Object getParameterValue(Method m, String parameterName,
0502: Object[] parameters) throws IllegalArgumentException {
0503: String[] names = getParameterNames(m);
0504:
0505: // Validate the input parameter array
0506: if (parameters.length != names.length)
0507: throw new IllegalArgumentException("Expected "
0508: + names.length + " parameters," + "Found "
0509: + parameters.length);
0510:
0511: // Finding the index of the matching parameter name
0512: int i = 0;
0513: while (i < names.length) {
0514: if (names[i].equals(parameterName))
0515: break;
0516: i++;
0517: }
0518: if (i == names.length)
0519: throw new IllegalArgumentException(
0520: "No method parameter with name: " + parameterName);
0521:
0522: // Return the parameter value at the matched index
0523: return parameters[i];
0524: }
0525:
0526: //
0527: // ControlBeanContext.getPropertyMap
0528: //
0529: public PropertyMap getControlPropertyMap() {
0530: //
0531: // Return a wrapped copy of the original bean property map, so any edits
0532: // don't impact the bean properties.
0533: //
0534: return new BeanPropertyMap(_bean.getPropertyMap());
0535: }
0536:
0537: //
0538: // ControlBeanContext.getService
0539: //
0540: public <T> T getService(Class<T> serviceClass, Object selector) {
0541: //
0542: // If the requested service is a ControlBeanContext instance, the current instance
0543: // can be returned.
0544: //
0545: if (serviceClass
0546: .equals(org.apache.beehive.controls.api.context.ControlBeanContext.class))
0547: return (T) this ;
0548:
0549: //
0550: // The parent BeanContext is responsible for providing requested services. If
0551: // no parent context is available or it is does not manage services, then no service.
0552: //
0553: BeanContext bc = getBeanContext();
0554: if (bc == null || !(bc instanceof BeanContextServices))
0555: return null;
0556:
0557: //
0558: // Call getService on the parent context, using this bean as the requestor and the
0559: // this context as the child context and ServicesRevoked event listener parameters.
0560: //
0561: try {
0562: return (T) ((BeanContextServices) bc).getService(this ,
0563: _bean, serviceClass, selector, this );
0564: } catch (TooManyListenersException tmle) {
0565: // This would be highly unusual... it implies that the registration for service
0566: // revocation notifications failed for some reason.
0567: throw new ControlException(
0568: "Unable to register for service events", tmle);
0569: }
0570: }
0571:
0572: //
0573: // ControlBeanContext.getControlHandle
0574: //
0575: public ControlHandle getControlHandle() {
0576: //
0577: // Find the associated ControlContainerContext, which provides a container-specific
0578: // implementation of ControlHandle
0579: //
0580: ControlBeanContext beanContext = this ;
0581: while (beanContext != null
0582: && !(beanContext instanceof ControlContainerContext))
0583: beanContext = (ControlBeanContext) beanContext
0584: .getBeanContext();
0585:
0586: if (beanContext == null)
0587: return null;
0588:
0589: //
0590: // Ask the container for a ControlHandle instance referencing the target bean
0591: //
0592: return ((ControlContainerContext) beanContext)
0593: .getControlHandle(_bean);
0594: }
0595:
0596: //
0597: // ControlBeanContext.getClassLoader
0598: //
0599: public java.lang.ClassLoader getClassLoader() {
0600: return getControlInterface().getClassLoader();
0601: }
0602:
0603: //
0604: // ControlBeanContext.addLifeCycleListener
0605: //
0606: synchronized public void addLifeCycleListener(LifeCycle listener) {
0607: if (_lifeCycleListeners == null) {
0608: _lifeCycleListeners = new Vector<LifeCycle>();
0609:
0610: //
0611: // Since bound/constrained property changes are exposed as lifecycle events, we
0612: // need to register ourselves as a listener for these events the first time a
0613: // lifecycle listener is added.
0614: //
0615: _bean.getPropertyChangeSupport().addPropertyChangeListener(
0616: this );
0617: _bean.getVetoableChangeSupport().addVetoableChangeListener(
0618: this );
0619: }
0620: _lifeCycleListeners.addElement(listener);
0621: }
0622:
0623: //
0624: // ControlBeanContext.removeLifeCycleListener
0625: //
0626: synchronized public void removeLifeCycleListener(LifeCycle listener) {
0627: if (_lifeCycleListeners != null)
0628: _lifeCycleListeners.removeElement(listener);
0629: }
0630:
0631: //
0632: // PropertyChangeListener.propertyChange
0633: //
0634: public void propertyChange(PropertyChangeEvent pce) {
0635: if (_lifeCycleListeners != null) {
0636: for (LifeCycle lifeCycleListener : _lifeCycleListeners) {
0637: lifeCycleListener.onPropertyChange(pce);
0638: }
0639: }
0640: }
0641:
0642: //
0643: // VetoableChangeListener.vetoableChange
0644: //
0645: public void vetoableChange(PropertyChangeEvent pce)
0646: throws PropertyVetoException {
0647: if (_lifeCycleListeners != null) {
0648: for (LifeCycle lifeCycleListener : _lifeCycleListeners) {
0649: lifeCycleListener.onVetoableChange(pce);
0650: }
0651: }
0652: }
0653:
0654: /* package */String getControlID() {
0655: if (_controlID != null || _bean == null)
0656: return _controlID;
0657:
0658: // Initially set to the local beans relative ID
0659: String id = _bean.getLocalID();
0660:
0661: // If there is a parent context, prepend its ID and the ID separator
0662: BeanContext bc = getBeanContext();
0663: if (bc != null && bc instanceof ControlBeanContext) {
0664: String parentID = ((ControlBeanContext) bc).getControlID();
0665: if (parentID != null) {
0666: id = parentID
0667: + org.apache.beehive.controls.api.bean.ControlBean.IDSeparator
0668: + id;
0669: }
0670: }
0671:
0672: // Cache the computed value
0673: _controlID = id;
0674:
0675: return id;
0676: }
0677:
0678: /**
0679: * Resets the composite control ID for this context and all children beneath it. This
0680: * can be used to invalidate cached values when necessary (for example, when a context
0681: * is reparented).
0682: */
0683: private void resetControlID() {
0684: _controlID = null;
0685: for (Object child : this ) {
0686: if (child instanceof ControlBeanContext)
0687: ((ControlBeanContext) child).resetControlID();
0688: }
0689: }
0690:
0691: //
0692: // BeanContextServices.getCurrentServiceClasses
0693: // Override the default implementation of getCurrentServiceClasses inherited from
0694: // java.beans.beancontext.BeanContextServicesSuppport. The reason for this is a bug/
0695: // flaw in its underlying implementation. It does not include any services exposed
0696: // by any nesting contexts. This is contradictory to the implementation of hasService()
0697: // and getService() which do delegate up to a parent context to find services if not
0698: // available on the local context. This means hasService() could return 'true' for a
0699: // service that isn't returned by getCurrentServiceClasses(), which seems like a bug.
0700: //
0701: synchronized public Iterator getCurrentServiceClasses() {
0702: Set classSet = new HashSet();
0703: BeanContextServices bcs = _beanContextServicesDelegate;
0704:
0705: while (bcs != null) {
0706: Iterator iter = bcs.getCurrentServiceClasses();
0707: while (iter.hasNext())
0708: classSet.add(iter.next());
0709:
0710: // Go up to the parent, if it is a service provider as well
0711: BeanContext bc = getBeanContext();
0712: if (bc instanceof BeanContextServices && bcs != bc)
0713: bcs = (BeanContextServices) bc;
0714: else
0715: bcs = null;
0716: }
0717: return classSet.iterator();
0718: }
0719:
0720: //
0721: // BeanContextServices.getCurrentServiceSelectors
0722: // Override getCurrentServiceSelectors for the same reason as above
0723: //
0724: public Iterator getCurrentServiceSelectors(Class serviceClass) {
0725: if (hasService(serviceClass))
0726: return _beanContextServicesDelegate
0727: .getCurrentServiceSelectors(serviceClass);
0728:
0729: BeanContext bc = getBeanContext();
0730: if (bc instanceof BeanContextServices)
0731: return ((BeanContextServices) bc)
0732: .getCurrentServiceSelectors(serviceClass);
0733:
0734: return null;
0735: }
0736:
0737: private synchronized void writeObject(ObjectOutputStream oos)
0738: throws IOException {
0739: oos.defaultWriteObject();
0740: if (_beanContextServicesDelegate instanceof java.beans.beancontext.BeanContextSupport) {
0741: ((java.beans.beancontext.BeanContextSupport) _beanContextServicesDelegate)
0742: .writeChildren(oos);
0743: } else if (_beanContextServicesDelegate instanceof org.apache.beehive.controls.runtime.webcontext.ControlBeanContextSupport) {
0744: ((org.apache.beehive.controls.runtime.webcontext.ControlBeanContextSupport) _beanContextServicesDelegate)
0745: .writeChildren(oos);
0746: } else
0747: assert false;
0748: }
0749:
0750: private synchronized void readObject(ObjectInputStream ois)
0751: throws IOException, ClassNotFoundException {
0752: ois.defaultReadObject();
0753:
0754: if (_beanContextServicesDelegate instanceof java.beans.beancontext.BeanContextSupport) {
0755: ((java.beans.beancontext.BeanContextSupport) _beanContextServicesDelegate)
0756: .readChildren(ois);
0757: } else if (_beanContextServicesDelegate instanceof org.apache.beehive.controls.runtime.webcontext.ControlBeanContextSupport) {
0758: ((org.apache.beehive.controls.runtime.webcontext.ControlBeanContextSupport) _beanContextServicesDelegate)
0759: .readChildren(ois);
0760: } else
0761: assert false;
0762:
0763: // Re-initialize a deserialized control hierarchy.
0764: initialize();
0765: }
0766:
0767: protected BeanContextServicesFactory getBeanContextServicesFactory() {
0768: return DEFAULT_BEAN_CONTEXT_SERVICES_FACTORY;
0769: }
0770:
0771: public boolean equals(Object o) {
0772: /* todo: make sure this logic is right / sufficient */
0773: if (this == o)
0774: return true;
0775:
0776: if (!(o instanceof org.apache.beehive.controls.api.context.ControlBeanContext))
0777: return false;
0778:
0779: return o instanceof ControlBeanContext
0780: && _beanContextServicesDelegate
0781: .equals(((ControlBeanContext) o)._beanContextServicesDelegate);
0782: }
0783:
0784: public int hashCode() {
0785: /* todo: make sure this logic is right / sufficient */
0786: int result;
0787: result = (_bean != null ? _bean.hashCode() : 0);
0788: result = 31
0789: * result
0790: + (_beanContextServicesDelegate != null ? _beanContextServicesDelegate
0791: .hashCode()
0792: : 0);
0793: return result;
0794: }
0795:
0796: /* --------------------------------------------------------------------------
0797:
0798: Implementation of java.beans.beancontext.BeanContextServices
0799:
0800: -------------------------------------------------------------------------- */
0801: public boolean addService(Class serviceClass,
0802: BeanContextServiceProvider serviceProvider) {
0803: return _beanContextServicesDelegate.addService(serviceClass,
0804: serviceProvider);
0805: }
0806:
0807: public void revokeService(Class serviceClass,
0808: BeanContextServiceProvider serviceProvider,
0809: boolean revokeCurrentServicesNow) {
0810: _beanContextServicesDelegate.revokeService(serviceClass,
0811: serviceProvider, revokeCurrentServicesNow);
0812: }
0813:
0814: public boolean hasService(Class serviceClass) {
0815: return _beanContextServicesDelegate.hasService(serviceClass);
0816: }
0817:
0818: public Object getService(BeanContextChild child, Object requestor,
0819: Class serviceClass, Object serviceSelector,
0820: BeanContextServiceRevokedListener bcsrl)
0821: throws TooManyListenersException {
0822: return _beanContextServicesDelegate.getService(child,
0823: requestor, serviceClass, serviceSelector, bcsrl);
0824: }
0825:
0826: public void releaseService(BeanContextChild child,
0827: Object requestor, Object service) {
0828: _beanContextServicesDelegate.releaseService(child, requestor,
0829: service);
0830: }
0831:
0832: public void addBeanContextServicesListener(
0833: BeanContextServicesListener bcsl) {
0834: _beanContextServicesDelegate
0835: .addBeanContextServicesListener(bcsl);
0836: }
0837:
0838: public void removeBeanContextServicesListener(
0839: BeanContextServicesListener bcsl) {
0840: _beanContextServicesDelegate
0841: .removeBeanContextServicesListener(bcsl);
0842: }
0843:
0844: public Object instantiateChild(String beanName) throws IOException,
0845: ClassNotFoundException {
0846: return _beanContextServicesDelegate.instantiateChild(beanName);
0847: }
0848:
0849: public InputStream getResourceAsStream(String name,
0850: BeanContextChild bcc) throws IllegalArgumentException {
0851: return _beanContextServicesDelegate.getResourceAsStream(name,
0852: bcc);
0853: }
0854:
0855: public URL getResource(String name, BeanContextChild bcc)
0856: throws IllegalArgumentException {
0857: return _beanContextServicesDelegate.getResource(name, bcc);
0858: }
0859:
0860: public void addBeanContextMembershipListener(
0861: BeanContextMembershipListener bcml) {
0862: _beanContextServicesDelegate
0863: .addBeanContextMembershipListener(bcml);
0864: }
0865:
0866: public void removeBeanContextMembershipListener(
0867: BeanContextMembershipListener bcml) {
0868: _beanContextServicesDelegate
0869: .removeBeanContextMembershipListener(bcml);
0870: }
0871:
0872: public BeanContext getBeanContext() {
0873: return _beanContextServicesDelegate.getBeanContext();
0874: }
0875:
0876: public void addPropertyChangeListener(String name,
0877: PropertyChangeListener pcl) {
0878: _beanContextServicesDelegate.addPropertyChangeListener(name,
0879: pcl);
0880: }
0881:
0882: public void removePropertyChangeListener(String name,
0883: PropertyChangeListener pcl) {
0884: _beanContextServicesDelegate.removePropertyChangeListener(name,
0885: pcl);
0886: }
0887:
0888: public void addVetoableChangeListener(String name,
0889: VetoableChangeListener vcl) {
0890: _beanContextServicesDelegate.addVetoableChangeListener(name,
0891: vcl);
0892: }
0893:
0894: public void removeVetoableChangeListener(String name,
0895: VetoableChangeListener vcl) {
0896: _beanContextServicesDelegate.removeVetoableChangeListener(name,
0897: vcl);
0898: }
0899:
0900: public int size() {
0901: return _beanContextServicesDelegate.size();
0902: }
0903:
0904: public boolean isEmpty() {
0905: return _beanContextServicesDelegate.isEmpty();
0906: }
0907:
0908: public boolean contains(Object o) {
0909: return _beanContextServicesDelegate.contains(o);
0910: }
0911:
0912: public Iterator iterator() {
0913: return _beanContextServicesDelegate.iterator();
0914: }
0915:
0916: public Object[] toArray() {
0917: return _beanContextServicesDelegate.toArray();
0918: }
0919:
0920: public Object[] toArray(Object[] a) {
0921: return _beanContextServicesDelegate.toArray(a);
0922: }
0923:
0924: public boolean containsAll(Collection c) {
0925: return _beanContextServicesDelegate.containsAll(c);
0926: }
0927:
0928: public boolean addAll(Collection c) {
0929: return _beanContextServicesDelegate.addAll(c);
0930: }
0931:
0932: public boolean removeAll(Collection c) {
0933: return _beanContextServicesDelegate.removeAll(c);
0934: }
0935:
0936: public boolean retainAll(Collection c) {
0937: return _beanContextServicesDelegate.retainAll(c);
0938: }
0939:
0940: public void clear() {
0941: _beanContextServicesDelegate.clear();
0942: }
0943:
0944: public void setDesignTime(boolean designTime) {
0945: _beanContextServicesDelegate.setDesignTime(designTime);
0946: }
0947:
0948: public boolean isDesignTime() {
0949: return _beanContextServicesDelegate.isDesignTime();
0950: }
0951:
0952: public boolean needsGui() {
0953: return _beanContextServicesDelegate.needsGui();
0954: }
0955:
0956: public void dontUseGui() {
0957: _beanContextServicesDelegate.dontUseGui();
0958: }
0959:
0960: public void okToUseGui() {
0961: _beanContextServicesDelegate.okToUseGui();
0962: }
0963:
0964: public boolean avoidingGui() {
0965: return _beanContextServicesDelegate.avoidingGui();
0966: }
0967:
0968: public void serviceAvailable(BeanContextServiceAvailableEvent bcsae) {
0969: _beanContextServicesDelegate.serviceAvailable(bcsae);
0970: }
0971:
0972: /* --------------------------------------------------------------------------
0973:
0974: Static / deprecated methods.
0975:
0976: -------------------------------------------------------------------------- */
0977:
0978: /**
0979: * Applies externally defined (via INTERCEPTOR_CONFIG_FILE) ordering priority for
0980: * controls interceptor services.
0981: *
0982: * @param interceptors
0983: * @return String[]
0984: * @deprecated Use {@link InterceptorUtils#prioritizeInterceptors(String[])} instead. This method will
0985: * be removed in the next point release.
0986: */
0987: public static String[] prioritizeInterceptors(String[] interceptors) {
0988: return InterceptorUtils.prioritizeInterceptors(interceptors);
0989: }
0990:
0991: /**
0992: * Returns the default binding based entirely upon annotations or naming conventions.
0993: * @param controlIntf the control interface class
0994: * @return the class name of the default control implementation binding
0995: * @deprecated Use {@link ControlUtils#getDefaultControlBinding(Class)} insated. This method will be
0996: * removed in the next point release.
0997: */
0998: public static String getDefaultControlBinding(Class controlIntf) {
0999: return ControlUtils.getDefaultControlBinding(controlIntf);
1000: }
1001:
1002: /**
1003: * Implements the default control implementation binding algorithm ( <InterfaceName> + "Impl" ). See
1004: * documentation for the org.apache.beehive.controls.api.bean.ControlInterface annotation.
1005: *
1006: * @param implBinding the value of the defaultBinding attribute returned from a ControlInterface annotation
1007: * @param controlClass the actual name of the interface decorated by the ControlInterface annotation
1008: * @return the resolved defaultBinding value
1009: * @deprecated Use {@link ControlUtils#resolveDefaultBinding(String, String)} insated. This method
1010: * will be removed in the next point release.
1011: */
1012: public static String resolveDefaultBinding(String implBinding,
1013: String controlClass) {
1014: return ControlUtils.resolveDefaultBinding(implBinding,
1015: controlClass);
1016: }
1017:
1018: /**
1019: * The ControlBeanContextProvider inner class acts as a single BeanContext service
1020: * provider for the ControlBeanContext service class. The implementation is simple,
1021: * because the runtime ControlBeanContext implementation class directly implements
1022: * this interface.
1023: */
1024: private static class ControlBeanContextProvider implements
1025: BeanContextServiceProvider {
1026: //
1027: // BeanContextServiceProvider.getService()
1028: //
1029: public Object getService(BeanContextServices bcs,
1030: Object requestor, Class serviceClass,
1031: Object serviceSelector) {
1032: //
1033: // Contextual services for a ControlBean is provided by the peer context
1034: // instance.
1035: //
1036: if (requestor instanceof ControlBean)
1037: return ((ControlBean) requestor)
1038: .getControlBeanContext();
1039:
1040: return null;
1041: }
1042:
1043: //
1044: // BeanContextServiceProvider.releaseService()
1045: //
1046: public void releaseService(BeanContextServices bcs,
1047: Object requestor, Object service) {
1048: // noop, because context exists whether referenced or not
1049: }
1050:
1051: //
1052: // BeanContextServiceProvider.getContextServiceSelectors()
1053: //
1054: public Iterator getCurrentServiceSelectors(
1055: BeanContextServices bcs, Class serviceClass) {
1056: return null; // no selectors
1057: }
1058: }
1059:
1060: /*package*/static abstract class BeanContextServicesFactory {
1061: protected abstract BeanContextServices instantiate(
1062: ControlBeanContext controlBeanContext);
1063: }
1064:
1065: private static final class DefaultBeanContextServicesFactory extends
1066: BeanContextServicesFactory {
1067: protected BeanContextServices instantiate(
1068: ControlBeanContext controlBeanContext) {
1069: return new java.beans.beancontext.BeanContextServicesSupport(
1070: controlBeanContext);
1071: }
1072: }
1073:
1074: /**
1075: * A singleton instance of the ControlBeanContextProvider class is that will be registered
1076: * on all ControlBeanContext instances. The provider can be a singleton because it is
1077: * completely stateless and thread-safe.
1078: */
1079: private static final ControlBeanContextProvider CONTROL_BEAN_CONTEXT_PROVIDER = new ControlBeanContextProvider();
1080:
1081: /**
1082: * A singleton instance of the BeanContextServicesFactory class that can be implemented by subclasses
1083: * to allow top-level Control containers to provide their own implementations of the
1084: * {@link java.beans.beancontext.BeanContextServices} interface. This field is considered an implementation
1085: * detail and should not be referenced directly.
1086: */
1087: private static final BeanContextServicesFactory DEFAULT_BEAN_CONTEXT_SERVICES_FACTORY = new DefaultBeanContextServicesFactory();
1088:
1089: /**
1090: * The ControlBean instance that this context is providing services for. This value can
1091: * be null, if the context instance is associated with top-level (non-control) context.
1092: */
1093: private ControlBean _bean;
1094:
1095: /**
1096: * Indicates whether this context's parent guarantees single-threaded behaviour.
1097: */
1098: private boolean _hasSingleThreadedParent = false;
1099:
1100: /**
1101: * Maps children by the local (relative) ID of the child to the actual bean instance.
1102: * This needs to be synchronized, because adds/removes/gets are not necessarily guaranteed
1103: * to happen within the scope of the global hierarchy lock. It would be relatively easy
1104: * to synchronize add/remove, since setBeanContext on the child is inside this lock scope,
1105: * but gets on the map are another story.
1106: */
1107: private Map<String, Object> _childMap = Collections
1108: .synchronizedMap(new HashMap<String, Object>());
1109:
1110: /**
1111: * Maintains a set of NameGenerators (for control ID generation) keyed by a
1112: * base prefix. The map itself is lazily constructed, so there is minimal
1113: * overhead of no id generation is needed in this context.
1114: */
1115: private Map<String, NameGenerator> _nameGenerators;
1116:
1117: /**
1118: * Maintains the list of lifecycle event listeners (if any) for this context.
1119: */
1120: private transient Vector<LifeCycle> _lifeCycleListeners;
1121:
1122: /**
1123: * Caches the full composite control ID, that includes the entire path from the root
1124: * ContainerContext to the associated bean. This value can be transient, since it
1125: * can be easily recomputed when needed.
1126: */
1127: private transient String _controlID;
1128:
1129: /**
1130: * Object that implements the java.beans.beancontext APIs from the JDK to provide compliance with the
1131: * JavaBeans BeanContext / BeanContextChild specification. The ControlBeanContext class uses
1132: * this object as a delegate to provide this functionality rather than extending the BeanContext
1133: * support classes directly. This allows for more flexibility in how the BeanContextServices (et al)
1134: * API implementations evolve over time.
1135: */
1136: private BeanContextServices _beanContextServicesDelegate;
1137: }
|