KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > beehive > controls > runtime > bean > ControlBean


1 package org.apache.beehive.controls.runtime.bean;
2 /*
3  * Copyright 2004 The Apache Software Foundation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * $Header:$
18  */

19
20 import java.beans.beancontext.BeanContext JavaDoc;
21 import java.beans.beancontext.BeanContextServices JavaDoc;
22 import java.beans.PropertyChangeSupport JavaDoc;
23 import java.beans.VetoableChangeSupport JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.ObjectOutputStream JavaDoc;
26 import java.lang.reflect.AnnotatedElement JavaDoc;
27 import java.lang.reflect.InvocationTargetException JavaDoc;
28 import java.lang.reflect.Method JavaDoc;
29 import java.util.*;
30 import java.util.concurrent.Semaphore JavaDoc;
31
32 import org.apache.beehive.controls.api.ControlException;
33 import org.apache.beehive.controls.api.versioning.VersionRequired;
34 import org.apache.beehive.controls.api.versioning.Version;
35 import org.apache.beehive.controls.api.bean.Threading;
36 import org.apache.beehive.controls.api.bean.ThreadingPolicy;
37 import org.apache.beehive.controls.api.bean.ControlImplementation;
38 import org.apache.beehive.controls.api.bean.ControlExtension;
39 import org.apache.beehive.controls.api.bean.ControlInterface;
40 import org.apache.beehive.controls.api.context.ControlThreadContext;
41 import org.apache.beehive.controls.api.events.EventRef;
42 import org.apache.beehive.controls.api.events.EventSet;
43 import org.apache.beehive.controls.api.properties.*;
44 import org.apache.beehive.controls.spi.svc.Interceptor;
45 import org.apache.beehive.controls.spi.svc.InterceptorPivotException;
46
47 /**
48  * The ControlBean class is an abstract base class for the JavaBean classes generated to support
49  * Workshop controls.
50  * <p>
51  * The ControlBean class indirectly implements BeanContextProxy; the ControlBeanContext that it contains/scopes
52  * acts as that proxy.
53  * <p>
54  * All support APIs (which may be called from derived derived subclasses or contextual services
55  * are generally marked as protected and have names that start with an underscore. This avoids the
56  * possibility that the name might conflict with a user-defined method on a control's public or
57  * extended (JCX) interface.
58  * <p>
59  * NOTE: Adding public methods should be done with great care; any such method becomes part of the
60  * public API, and occupies namespace for all controls.
61  */

62 abstract public class ControlBean
63                       implements org.apache.beehive.controls.api.bean.ControlBean
64 {
65     /**
66      *
67      * @param context the containing ControlBeanContext. May be null, in which case the bean will attempt to
68      * associate with an active context at runtime (via thread-locals).
69      * @param id the local ID for the control, scoped to the control bean context.
70      * @param initProperties a PropertyMap containing initial properties for the control
71      * @param controlIntf the control interface or extension directly implemented by the control bean
72      */

73     protected ControlBean(org.apache.beehive.controls.api.context.ControlBeanContext context,
74                           String JavaDoc id, PropertyMap initProperties, Class JavaDoc controlIntf)
75     {
76         super();
77
78         _localID = id;
79         _controlIntf = controlIntf;
80
81         // Create the context that acts as the BeanContextProxy for this bean (the context that this bean _defines_).
82
_cbc = new ControlBeanContext( this );
83
84         //
85
// If no containing context was specified during construction, see if there is a current
86
// active container context and implicitly associated the control with it.
87
//
88
if (context == null)
89             context = ControlThreadContext.getContext();
90
91         //
92
// Associate this bean with the context. Beans may run without a context!
93
// Note that the add() call has the side-effect of calling ControlBean.setBeanContext(), which does
94
// additional setup work, so we make sure we always call that anyways!
95
//
96
if (context != null)
97             context.add(this);
98         else
99             setBeanContext(null);
100
101         //
102
// Get the default map for the control class. This contains the default property
103
// values for all beans of the class.
104
//
105
PropertyMap classMap = getAnnotationMap(controlIntf);
106         if (initProperties != null)
107         {
108             //
109
// If the initialization map derives its values from a Java annotated element,
110
// then allow container overrides on this element to be applied. This will also
111
// coelesce maps referencing the same element.
112
//
113
AnnotatedElement JavaDoc annotElem = null;
114             if (initProperties instanceof AnnotatedElementMap)
115             {
116                 annotElem = ((AnnotatedElementMap)initProperties).getAnnotatedElement();
117                 initProperties = getAnnotationMap(annotElem);
118             }
119
120             //
121
// If an initial property map was provided, set it to delegate to the default
122
// map, and then create a wrapper map around it for storing per instance
123
// properties.
124
//
125
if (annotElem != controlIntf)
126                 initProperties.setDelegateMap(classMap);
127             _properties = new BeanPropertyMap(initProperties);
128         }
129         else
130         {
131             //
132
// If no initial map was provided, simply create an empty map wrapping the
133
// control class default.
134
//
135
_properties = new BeanPropertyMap(classMap);
136         }
137
138     }
139
140     /**
141      * Configure this bean to be thread-safe given the threading settings of the impl class and
142      * the outer container.
143      */

144     private void ensureThreadingBehaviour()
145     {
146         //
147
// If the implementation class requires a guarantee of single-threaded behavior and the
148
// outer container does not guarantee it, then enable invocation locking on this
149
// bean instance.
150
//
151
if (hasSingleThreadedImpl() && ! _cbc.hasSingleThreadedParent())
152             _invokeLock = new Semaphore JavaDoc(1, true);
153         else
154             _invokeLock = null;
155     }
156     /**
157      * Return the BeanContextService proxy associated with this bean instance
158      */

159     final public ControlBeanContext getBeanContextProxy()
160     {
161         return _cbc;
162     }
163
164     /**
165      * Returns the nesting BeanContext for this ControlBean. This is thread-safe even though it
166      * is not synchronized.
167      */

168     final public BeanContext JavaDoc getBeanContext()
169     {
170         //
171
// Indirect through the bean proxy for this control bean and up to its parent nesting
172
// context. Both these calls (getBeanContextProxy() and getBeanContext()) are, and must
173
// remain, thread-safe.
174
//
175
return getBeanContextProxy().getBeanContext();
176     }
177
178     /**
179      * Called by the BeanContextProxy (_cbc) whenever the _parent_ context containing this control bean is
180      * changed. This is the place to do any initialization (or reinitialization) that is dependent
181      * upon attributes of the container for the ControlBean.
182      *
183      * Note: this is called in the ControlBean ctor, when a parent context calls add() on the nascent
184      * bean.
185      *
186      * @param bc the new parent context containing this control bean (not _cbc)
187      */

188     final public synchronized void setBeanContext(BeanContext JavaDoc bc)
189     {
190         ensureThreadingBehaviour();
191     }
192
193     /**
194      * Returns the control ID for this control
195      */

196     final public String JavaDoc getControlID()
197     {
198         return _cbc.getControlID();
199     }
200
201     /**
202      * Returns the public interface for this control.
203      */

204     final public Class JavaDoc getControlInterface()
205     {
206         return _controlIntf;
207     }
208
209     /**
210      * Returns true if the implementation class for this ControlBean requires the framework
211      * to ensure thread-safety for it.
212      */

213     /*package*/ boolean hasSingleThreadedImpl()
214     {
215         return _threadingPolicy == ThreadingPolicy.SINGLE_THREADED;
216     }
217
218     /**
219      * Obtains an instance of the appropriate ImplInitializer class
220      */

221     protected synchronized ImplInitializer getImplInitializer()
222     {
223         if (_implInitializer == null)
224         {
225             try
226             {
227                 Class JavaDoc initClass = _implClass.getClassLoader().loadClass(
228                                         _implClass.getName() + "Initializer");
229                 _implInitializer = (ImplInitializer)initClass.newInstance();
230             }
231             catch (Exception JavaDoc e)
232             {
233                 throw new ControlException("Control initialization failure", e);
234             }
235         }
236         return _implInitializer;
237     }
238
239     /**
240      * Returns the target control instance associated with this ControlBean, performing lazy
241      * instantiation and initialization of the instance.
242      *
243      * REVIEW: could probably improve the granularity of locking here, but start w/ just
244      * synchronizing the entire fn.
245      */

246     public synchronized Object JavaDoc ensureControl()
247     {
248         if (_control == null)
249         {
250             //
251
// See if the property map specifies an implementation class for the control;
252
// if not, use default binding.
253
//
254

255             String JavaDoc implBinding = null;
256             BaseProperties bp = _properties.getPropertySet( BaseProperties.class );
257             if ( bp != null )
258                 implBinding = bp.controlImplementation();
259             else
260                 implBinding = ControlBeanContext.getDefaultControlBinding(_controlIntf);
261
262             try
263             {
264                 _implClass = _controlIntf.getClassLoader().loadClass(implBinding);
265
266                 //
267
// Validate that the specified implementation class has an @ControlImplementation
268
// annotation, else downstream requirements (such as having a valid control init
269
// class) will not be met.
270
//
271
if (_implClass.getAnnotation(ControlImplementation.class) == null)
272                 {
273                     throw new ControlException("@org.apache.beehive.controls.api.bean.ControlImplementation annotation is missing from control implementation class: " + _implClass.getName());
274                 }
275             }
276             catch (ClassNotFoundException JavaDoc cnfe)
277             {
278                 throw new ControlException("Unable to load control implementation: " + implBinding, cnfe);
279             }
280
281             //
282
// Cache the threading policy associated with the impl
283
//
284
Threading thr = (Threading)_implClass.getAnnotation(Threading.class);
285             if ( thr != null )
286                 _threadingPolicy = thr.value();
287             else
288                 _threadingPolicy = ThreadingPolicy.SINGLE_THREADED; // default to single-threaded
289

290             ensureThreadingBehaviour();
291
292             try
293             {
294                 //
295
// Create and initialize the new instance
296
//
297
_control = _implClass.newInstance();
298
299                 try
300                 {
301                     getImplInitializer().initialize(this, _control);
302                     _hasServices = true;
303                 }
304                 catch (Exception JavaDoc e)
305                 {
306                     throw new ControlException("Control initialization failure", e);
307                 }
308
309                 //
310
// Once the control is initialized, then allow the associated context
311
// to do any initialization.
312
//
313
ControlBeanContext cbcs = (ControlBeanContext)getBeanContextProxy();
314                 cbcs.initializeControl();
315             }
316             catch (RuntimeException JavaDoc re) { throw re; } // never mask RuntimeExceptions
317
catch (Exception JavaDoc e)
318             {
319                 throw new ControlException("Unable to create control instance", e);
320             }
321         }
322
323         //
324
// If the implementation instance does not currently have contextual services, they
325
// are lazily restored here.
326
//
327
if (!_hasServices)
328         {
329             getImplInitializer().initServices(this, _control);
330             _hasServices = true;
331         }
332
333         return _control;
334     }
335
336     /**
337      * Returns the implementation instance associated with this ControlBean.
338      */

339     /* package */ Object JavaDoc getImplementation() { return _control; }
340
341     /**
342      * The preinvoke method is called before all operations on the control. In addition to
343      * providing a basic hook for logging, context initialization, resource management,
344      * and other common services, it also provides a hook for interceptors.
345      */

346     protected void preInvoke(Method JavaDoc m, Object JavaDoc [] args, String JavaDoc [] interceptorNames)
347         throws InterceptorPivotException
348     {
349         //
350
// If the implementation expects single threaded behavior and our container does
351
// not guarantee it, then enforce it locally here
352
//
353
if (_invokeLock != null)
354         {
355             try { _invokeLock.acquire(); } catch (InterruptedException JavaDoc ie) { }
356         }
357
358         //
359
// Process interceptors
360
//
361
if ( interceptorNames != null )
362         {
363             ControlBeanContext cbc = getControlBeanContext();
364
365             for ( String JavaDoc n : interceptorNames )
366             {
367                 Interceptor i = ensureInterceptor( n );
368                 try
369                 {
370                     i.preInvoke( this, m, args );
371                 }
372                 catch (InterceptorPivotException ipe)
373                 {
374                     ipe.setInterceptorName(n);
375                     throw ipe;
376                 }
377             }
378         }
379
380         Vector<InvokeListener> invokeListeners = getInvokeListeners();
381         if (invokeListeners.size() > 0)
382         {
383             for (InvokeListener listener : invokeListeners)
384                 listener.preInvoke(m, args);
385         }
386     }
387
388     /**
389      * The preinvoke method is called before all operations on the control. It is the basic
390      * hook for logging, context initialization, resource management, and other common
391      * services
392      */

393     protected void preInvoke(Method JavaDoc m, Object JavaDoc [] args)
394     {
395         try
396         {
397             preInvoke(m, args, null);
398         }
399         catch (InterceptorPivotException ipe)
400         {
401             //this will never happen because no interceptor is passed.
402
}
403     }
404
405     /**
406      * The postInvoke method is called after all operations on the control. In addition to
407      * providing the basic hook for logging, context initialization, resource management, and other common
408      * services, it also provides a hook for interceptors. During preInvoke, interceptors will be
409      * called in the order that they are in the list. During postInvoke, they will be called in the
410      * reverse order. Here is an example of the call sequence with I1, I2, and I3 being interceptors in the list:
411      *
412      * I1.preInvoke() -> I2.preInvoke() -> I3.preInvoke() -> invoke method
413      * |
414      * I1.postInvoke() <- I2.postInvoke() <- I3.postInvoke() <---
415      *
416      * In the event that an interceptor in the list pivoted during preInvoke, the "pivotedInterceptor"
417      * parameter indicates the interceptor that had pivoted, and the interceptors succeeding it in the list will
418      * not be called during postInvoke.
419      */

420     protected void postInvoke(Method JavaDoc m, Object JavaDoc [] args, Object JavaDoc retval, Throwable JavaDoc t, String JavaDoc [] interceptorNames, String JavaDoc pivotedInterceptor)
421     {
422         try
423         {
424             //
425
// Process interceptors
426
//
427
if ( interceptorNames != null )
428             {
429                 ControlBeanContext cbc = getControlBeanContext();
430
431                 for (int cnt = interceptorNames.length-1; cnt >= 0; cnt-- )
432                 {
433                     String JavaDoc n = interceptorNames[cnt];
434                     if (pivotedInterceptor == null || n.equals(pivotedInterceptor))
435                     {
436                         pivotedInterceptor = null;
437                         Interceptor i = ensureInterceptor( n );
438                         i.postInvoke( this, m, args, retval, t );
439                     }
440                 }
441             }
442
443             Vector<InvokeListener> invokeListeners = getInvokeListeners();
444             if (invokeListeners.size() > 0)
445             {
446                 for (InvokeListener listener : invokeListeners)
447                     listener.postInvoke(retval, t);
448             }
449         }
450         finally
451         {
452             //
453
// Release any lock obtained above in preInvoke
454
//
455
if (_invokeLock != null)
456                 _invokeLock.release();
457         }
458     }
459
460     /**
461      * The postInvoke method is called after all operations on the control. It is the basic
462      * hook for logging, context initialization, resource management, and other common
463      * services.
464      */

465     protected void postInvoke(Method JavaDoc m, Object JavaDoc [] args, Object JavaDoc retval, Throwable JavaDoc t)
466     {
467         postInvoke(m, args, retval, t, null, null);
468     }
469
470     
471     /**
472      * Sets the EventNotifier for this ControlBean
473      */

474     protected <T> void setEventNotifier(Class JavaDoc<T> eventSet, T notifier)
475     {
476         _notifiers.put(eventSet,notifier);
477
478         //
479
// Register this notifier for all EventSet interfaces up the interface inheritance
480
// hiearachy as well
481
//
482
List<Class JavaDoc> superEventSets = new ArrayList<Class JavaDoc>();
483         getSuperEventSets(eventSet, superEventSets);
484         Iterator<Class JavaDoc> i = superEventSets.iterator();
485         while (i.hasNext())
486         {
487             Class JavaDoc superEventSet = i.next();
488             _notifiers.put(superEventSet,notifier);
489         }
490     }
491
492     /**
493      * Finds all of the EventSets extended by the input EventSet, and adds them to
494      * the provided list.
495      * @param eventSet
496      * @param superEventSets
497      */

498     private void getSuperEventSets(Class JavaDoc eventSet, List<Class JavaDoc> superEventSets)
499     {
500         Class JavaDoc[] superInterfaces = eventSet.getInterfaces();
501         if (superInterfaces != null)
502         {
503             for (int i=0; i < superInterfaces.length; i++)
504             {
505                 Class JavaDoc superInterface = superInterfaces[i];
506                 if (superInterface.isAnnotationPresent(EventSet.class))
507                 {
508                     superEventSets.add(superInterface);
509
510                     // Continue traversing up the EventSet inheritance hierarchy
511
getSuperEventSets(superInterface, superEventSets);
512                 }
513             }
514         }
515     }
516
517     /**
518      * Returns an EventNotifier/UnicastEventNotifier for this ControlBean for the target event set
519      */

520     protected <T> T getEventNotifier(Class JavaDoc<T> eventSet)
521     {
522         return (T)_notifiers.get(eventSet);
523     }
524
525     /**
526      * Returns the list of InvokeListeners for this ControlBean
527      */

528     /* package */ Vector<InvokeListener> getInvokeListeners()
529     {
530         if (_invokeListeners == null)
531             _invokeListeners = new Vector<InvokeListener>();
532         return _invokeListeners;
533     }
534
535     /**
536      * Registers a new InvokeListener for this ControlBean.
537      */

538     /* package */ void addInvokeListener(InvokeListener invokeListener)
539     {
540         getInvokeListeners().addElement(invokeListener);
541     }
542
543     /**
544      * Deregisters an existing InvokeListener for this ControlBean.
545      */

546     /* package */ void removeInvokeListener(InvokeListener invokeListener)
547     {
548         getInvokeListeners().removeElement(invokeListener);
549     }
550
551     /**
552      * Returns the local (parent-relative) ID for this ControlBean
553      */

554     protected String JavaDoc getLocalID()
555     {
556         return _localID;
557     }
558
559     /**
560      * Set the local (parent-relative) ID for this ControlBean. It has package access because
561      * the local ID should only be set from within the associated context, and only when the
562      * bean is currently anonymous (hence the assertion below)
563      */

564     /* package */ void setLocalID(String JavaDoc localID)
565     {
566         assert _localID == null; // should only set if not already set!
567
_localID = localID;
568     }
569
570
571     /**
572      * Returns the bean context instance associated with the this bean, as opposed to the
573      * parent context returned by the public getBeanContext() API.
574      */

575     public ControlBeanContext getControlBeanContext()
576     {
577         //
578
// The peer context instance is the context provider for this ControlBean
579
//
580
return (ControlBeanContext)getBeanContextProxy();
581     }
582
583     /**
584      * Locates and obtains a context service from the BeanContextServices instance
585      * supporting this bean.
586      *
587      * The base design for the BeanContextServicesSupport is that it will delegate up to peers
588      * in a nesting context, so a nested control bean will look 'up' to find a service provider.
589      */

590     protected Object JavaDoc getControlService(Class JavaDoc serviceClass, Object JavaDoc selector)
591                      throws TooManyListenersException
592
593     {
594         //
595
// Get the associated context object, then use it to locate the (parent) bean context.
596
// Services are always provided by the parent context.
597
//
598
ControlBeanContext cbc = getControlBeanContext();
599         BeanContext JavaDoc bc = cbc.getBeanContext();
600         if (bc == null || !(bc instanceof BeanContextServices JavaDoc))
601             throw new ControlException("Can't locate service context: " + bc);
602
603         //
604
// Call getService on the parent context, using this bean as the requestor and the
605
// associated peer context instance as the child and event listener parameters.
606
//
607
return ((BeanContextServices JavaDoc)bc).getService(cbc, this, serviceClass, selector, cbc);
608     }
609
610     /**
611      * Sets a property on the ControlBean instance. All generated property setter methods
612      * will delegate down to this method.
613      */

614     protected void setControlProperty(PropertyKey key, Object JavaDoc o)
615     {
616         AnnotationConstraintValidator.validate(key, o);
617         _properties.setProperty(key, o);
618     }
619
620     /**
621      * Dispatches the requested operation event on the ControlBean.
622      * @see org.apache.beehive.controls.runtime.bean.ControlContainerContext#dispatchEvent
623      */

624     /* package */ Object JavaDoc dispatchEvent(EventRef event, Object JavaDoc [] args)
625                          throws IllegalAccessException JavaDoc,IllegalArgumentException JavaDoc,
626                                 InvocationTargetException JavaDoc
627     {
628         ensureControl();
629
630         //
631
// Translate the EventRef back to an actual event method on the ControlInterface
632
//
633
Class JavaDoc controlInterface = getControlInterface();
634         Method JavaDoc method = event.getEventMethod(controlInterface);
635         
636         //
637
// Locate the target of the event
638
//
639
Object JavaDoc eventTarget = null;
640         if (method.getDeclaringClass().isAssignableFrom(_control.getClass()))
641         {
642             //
643
// If the control implementation implements that EventSet interface, then
644
// dispatch the event directly to it, and allow it do make the decision about
645
// how/when to dispatch to any external listeners (via a @Client notifier
646
// instance)
647
//
648
eventTarget = _control;
649         }
650         else
651         {
652             //
653
// The associated control implementation does not directly handle the event,
654
// so find the event notifier instance for the EventSet interface associated
655
// with the method.
656
//
657
eventTarget = _notifiers.get(method.getDeclaringClass());
658             if (eventTarget == null)
659                 throw new IllegalArgumentException JavaDoc("No event notifier found for " + event);
660         }
661
662         //
663
// Dispatch the event
664
//
665
return method.invoke(eventTarget, args);
666     }
667
668     /**
669      * Returns a property on the ControlBean instance. This version does not coerce
670      * an annotation type property from a PropertyMap to a proxy instance of the
671      * type.
672      */

673     protected Object JavaDoc getRawControlProperty(PropertyKey key)
674     {
675         return _properties.getProperty(key);
676     }
677
678     /**
679      * Returns a property on the ControlBean instance. All generated property getter methods
680      * will delegate down to this method
681      */

682     protected Object JavaDoc getControlProperty(PropertyKey key)
683     {
684         Object JavaDoc value = getRawControlProperty(key);
685
686         // If the held value is a PropertyMap, then wrap it in an annotation proxy of
687
// the expected type.
688
if (value instanceof PropertyMap)
689         {
690             PropertyMap map = (PropertyMap)value;
691             value = PropertySetProxy.getProxy(map.getMapClass(), map);
692         }
693
694         return value;
695     }
696
697     /**
698      * Returns the local cache for ControlBean property maps.
699      */

700     abstract protected Map getPropertyMapCache();
701
702     /**
703      * Returns the PropertyMap containing values associated with an AnnotatedElement. Elements
704      * that are associated with the bean's Control interface will be locally cached.
705      */

706     protected PropertyMap getAnnotationMap(AnnotatedElement JavaDoc annotElem)
707     {
708         Map annotCache = getPropertyMapCache();
709
710         // If in the cache already , just return it
711
if (annotCache.containsKey(annotElem))
712             return (PropertyMap)annotCache.get(annotElem);
713
714         //
715
// Ask the associated ControlBeanContext to locate and initialize a PropertyMap, then
716
// store it in the local cache.
717
//
718
PropertyMap map = getControlBeanContext().getAnnotationMap(annotElem);
719         annotCache.put(annotElem, map);
720
721         return map;
722     }
723
724     /**
725      * Returns the property map containing the properties for the bean
726      */

727     /* package */ BeanPropertyMap getPropertyMap()
728     {
729         return _properties;
730     }
731
732     /**
733      * This protected version is only available to concrete subclasses that expose bound
734      * property support. This method is synchronized to enable lazy instantiation, in
735      * the belief that is a bigger win to avoid allocating when there are no listeners
736      * than it is to introduce synchronization overhead on access.
737      */

738     synchronized protected PropertyChangeSupport JavaDoc getPropertyChangeSupport()
739     {
740         if (_changeSupport == null)
741             _changeSupport = new PropertyChangeSupport JavaDoc(this);
742
743         return _changeSupport;
744     }
745
746     /**
747      * Delivers a PropertyChangeEvent to any registered PropertyChangeListeners associated
748      * with the property referenced by the specified key.
749      *
750      * This method *should not* be synchronized, as the PropertyChangeSupport has its own
751      * built in synchronization mechanisms.
752      */

753     protected void firePropertyChange(PropertyKey propertyKey, Object JavaDoc oldValue, Object JavaDoc newValue)
754     {
755         // No change support instance means no listeners
756
if (_changeSupport == null)
757             return;
758
759         _changeSupport.firePropertyChange(propertyKey.getPropertyName(), oldValue, newValue);
760     }
761
762     /**
763      * This protected version is only available to concrete subclasses that expose bound
764      * property support. This method is synchronized to enable lazy instantiation, in
765      * the belief that is a bigger win to avoid allocating when there are no listeners
766      * than it is to introduce synchronization overhead on access.
767      */

768     synchronized protected VetoableChangeSupport JavaDoc getVetoableChangeSupport()
769     {
770         if (_vetoSupport == null)
771             _vetoSupport = new VetoableChangeSupport JavaDoc(this);
772
773         return _vetoSupport;
774     }
775
776     /**
777      * Delivers a PropertyChangeEvent to any registered VetoableChangeListeners associated
778      * with the property referenced by the specified key.
779      *
780      * This method *should not* be synchronized, as the VetoableChangeSupport has its own
781      * built in synchronization mechanisms.
782      */

783     protected void fireVetoableChange(PropertyKey propertyKey, Object JavaDoc oldValue, Object JavaDoc newValue)
784                    throws java.beans.PropertyVetoException JavaDoc
785     {
786         // No veto support instance means no listeners
787
if (_vetoSupport == null)
788             return;
789
790         _vetoSupport.fireVetoableChange(propertyKey.getPropertyName(), oldValue, newValue);
791     }
792
793     /**
794      * Returns the parameter names for a method on the ControlBean. Actual mapping is done
795      * by generated subclasses, so if we reach the base ControlBean implementation, then
796      * no parameter names are available for the target method.
797      */

798     protected String JavaDoc [] getParameterNames(Method JavaDoc m)
799     {
800         throw new IllegalArgumentException JavaDoc("No parameter name data for " + m);
801     }
802
803     /**
804      * Computes the most derived ControlInterface for the specified ControlExtension.
805      * @param controlIntf
806      * @return the most derived ControlInterface
807      */

808     public static Class JavaDoc getMostDerivedInterface(Class JavaDoc controlIntf)
809     {
810         while (controlIntf.isAnnotationPresent(ControlExtension.class))
811         {
812             Class JavaDoc [] intfs = controlIntf.getInterfaces();
813             boolean found = false;
814             for (int i = 0; i < intfs.length; i++)
815             {
816                 if (intfs[i].isAnnotationPresent(ControlExtension.class) ||
817                     intfs[i].isAnnotationPresent(ControlInterface.class))
818                 {
819                     controlIntf = intfs[i];
820                     found = true;
821                     break;
822                 }
823             }
824             if (!found)
825             {
826                 throw new ControlException("Can't find base control interface for " + controlIntf);
827             }
828         }
829         return controlIntf;
830     }
831
832     /**
833      * Enforces the VersionRequired annotation at runtime (called from each ControlBean).
834      * @param intfName
835      * @param version
836      * @param versionRequired
837      */

838     protected static void enforceVersionRequired(String JavaDoc intfName, Version version, VersionRequired versionRequired)
839     {
840         if ( versionRequired != null )
841         {
842             int majorRequired = versionRequired.major();
843             int minorRequired = versionRequired.minor();
844
845             if ( majorRequired < 0 ) // no real version requirement
846
return;
847
848             int majorPresent = -1;
849             int minorPresent = -1;
850             if ( version != null )
851             {
852                 majorPresent = version.major();
853                 minorPresent = version.minor();
854
855                 if ( majorRequired <= majorPresent &&
856                      (minorRequired < 0 || minorRequired <= minorPresent) )
857                 {
858                     // Version requirement is satisfied
859
return;
860                 }
861             }
862
863             //
864
// Version requirement failed
865
//
866
throw new ControlException( "Control extension " + intfName + " fails version requirement: requires interface version " +
867                     majorRequired + "." + minorRequired + ", found interface version " +
868                     majorPresent + "." + minorPresent + "." );
869         }
870     }
871
872
873     /**
874      * Implementation of the Java serialization writeObject method
875      */

876     private synchronized void writeObject(ObjectOutputStream JavaDoc oos)
877                               throws IOException JavaDoc
878     {
879         if (_control != null)
880         {
881             //
882
// If the implementation class is marked as transient/stateless, then reset the
883
// reference to it prior to serialization. A new instance will be created by
884
// ensureControl() upon first use after deserialization.
885
// If the implementation class is not transient, then invoke the ImplInitializer
886
// resetServices method to reset all contextual service references to null, as
887
// contextual services should never be serializated and always reassociated on
888
// deserialization.
889
//
890
ControlImplementation implAnnot = (ControlImplementation)_implClass.getAnnotation(ControlImplementation.class);
891             assert implAnnot != null;
892             if (implAnnot.isTransient())
893             {
894                 _control = null;
895             }
896             else
897             {
898                 getImplInitializer().resetServices(this, _control);
899                 _hasServices = false;
900             }
901         }
902
903         oos.defaultWriteObject();
904     }
905
906     /**
907      * Called during XMLDecoder reconstruction of a ControlBean.
908      */

909     public void decodeImpl(Object JavaDoc impl)
910     {
911         if (impl != _control)
912             throw new ControlException("Cannot change implementation");
913     };
914
915     /**
916      * Retrieves interceptor instances, creates them lazily.
917      */

918     protected Interceptor ensureInterceptor( String JavaDoc n )
919     {
920         Interceptor i = null;
921         if ( _interceptors == null )
922         {
923             _interceptors = new HashMap<String JavaDoc,Interceptor>();
924         }
925         else
926         {
927             i = _interceptors.get( n );
928         }
929
930         if ( i == null )
931         {
932             try
933             {
934                 i = (Interceptor) getControlService( getControlBeanContext().getClassLoader().loadClass( n ), null );
935             }
936             catch ( Exception JavaDoc e )
937             {
938                 // Couldn't instantiate the desired service; usually this is because the service interface itself
939
// isn't present on this system at runtime (ClassNotFoundException), or if the container of the
940
// control didn't registers the service.
941

942                 // TODO log a message here to that effect, but just swallow the exception for now.
943
}
944             finally
945             {
946                 // We want to always return an interceptor, so if we can't get the one we want, we'll substitute
947
// a "null" interceptor that does nothing.
948
if ( i == null)
949                     i = new NullInterceptor();
950
951                 _interceptors.put( n, i );
952             }
953         }
954         return i;
955     }
956
957     /**
958      * The "null" interceptor that does nothing. Used when a specific interceptor
959      * is unavailable at runtime.
960      */

961     static private class NullInterceptor implements Interceptor
962     {
963         public void preInvoke( org.apache.beehive.controls.api.bean.ControlBean cb, Method JavaDoc m, Object JavaDoc [] args ) {}
964         public void postInvoke( org.apache.beehive.controls.api.bean.ControlBean cb, Method JavaDoc m, Object JavaDoc [] args, Object JavaDoc retval, Throwable JavaDoc t) {}
965         public void preEvent( org.apache.beehive.controls.api.bean.ControlBean cb, Class JavaDoc eventSet, Method JavaDoc m, Object JavaDoc [] args) {}
966         public void postEvent( org.apache.beehive.controls.api.bean.ControlBean cb, Class JavaDoc eventSet, Method JavaDoc m, Object JavaDoc [] args ) {}
967     }
968
969     /** BEGIN unsynchronized fields */
970
971     /**
972      * The following fields are initialized in the constructor and never subsequently changed,
973      * so they are safe for unsynchronized read access
974      */

975
976     /**
977      * The control implementation class bound to this ControlBean
978      */

979     protected Class JavaDoc _implClass;
980
981     /**
982      * The threading policy associated with the control implementation wrapped by this
983      * ControlBean. Initialized to MULTI_THREADED in order to assume multi-threadedness
984      * until a bean is associated with a specific (potentially single-threaded) implementation.
985      */

986     transient private ThreadingPolicy _threadingPolicy = ThreadingPolicy.MULTI_THREADED;
987
988     /**
989      * Contains the per-instance properties set for this ControlBean.
990      */

991     private BeanPropertyMap _properties;
992
993     /** END unsynchronized fields */
994
995     /* BEGIN synchronized fields */
996
997     /*
998      * The following fields must be:
999      * 1) only written in synchronized methods or (unsynchronized) constructors
1000     * 2) only read in synchronized methods or methods that are safe wrt the values changing during
1001     * execution.
1002     */

1003
1004    /**
1005     * The control implementation instance wrapped by this ControlBean
1006     */

1007    private Object JavaDoc _control;
1008
1009    /**
1010     * The control bean context instance associated with this ControlBean
1011     */

1012    private ControlBeanContext _cbc;
1013
1014    /**
1015     * An ImplInitializer instances used to initialize/reset the state of the associated
1016     * implementation instance.
1017     */

1018    transient private ImplInitializer _implInitializer;
1019
1020    /**
1021     * Indicates whether the contextual services associated with the bean have been
1022     * fully initialized.
1023     */

1024    transient private boolean _hasServices = false;
1025
1026    /**
1027     * Used to guarantee single threaded invocation when required. If the
1028     * outer container provides the guarantee or the implementation itself
1029     * is threadsafe, then the value will be null.
1030     */

1031    transient private Semaphore JavaDoc _invokeLock;
1032
1033    /**
1034     * This field manages PropertyChangeListeners (if supporting bound properties).
1035     */

1036    private PropertyChangeSupport JavaDoc _changeSupport;
1037
1038    /**
1039     * This field manages VetoabbleChangeListeners (if supporting constrained properties)
1040     */

1041    private VetoableChangeSupport JavaDoc _vetoSupport;
1042
1043    /** END synchronized fields */
1044
1045    /**
1046     * The (context relative) control ID associated with this instance
1047     */

1048    private String JavaDoc _localID;
1049
1050    /**
1051     * The public control interface associated with this ControlBean
1052     */

1053    private Class JavaDoc _controlIntf;
1054
1055    /**
1056     * This field manages the register listener list(s) associated with event set interfaces
1057     * for the ControlBean. The value objects are either UnicastEventNotifier or EventNotifier
1058     * instances, depending upon whether the associated EventSet interface is unicast or
1059     * multicast.
1060     */

1061    private HashMap<Class JavaDoc, Object JavaDoc> _notifiers = new HashMap<Class JavaDoc,Object JavaDoc>();
1062
1063    /**
1064     * Maintains the list of callback event listeners (if any) for this ControlBean.
1065     */

1066    transient private Vector<InvokeListener> _invokeListeners;
1067
1068    /**
1069     * HashMap to hold interceptor impl instances.
1070     * Populated lazily. Maps interceptor interface name to impl.
1071     */

1072    transient private HashMap<String JavaDoc,Interceptor> _interceptors;
1073}
1074
Popular Tags