001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019:
020: package org.apache.beehive.controls.runtime.webcontext;
021:
022: import java.beans.Beans;
023: import java.beans.PropertyChangeEvent;
024: import java.beans.PropertyChangeListener;
025: import java.beans.PropertyVetoException;
026: import java.beans.VetoableChangeListener;
027: import java.beans.Visibility;
028: import java.beans.beancontext.BeanContext;
029: import java.beans.beancontext.BeanContextChild;
030: import java.beans.beancontext.BeanContextMembershipEvent;
031: import java.beans.beancontext.BeanContextMembershipListener;
032: import java.beans.beancontext.BeanContextProxy;
033: import java.io.IOException;
034: import java.io.InputStream;
035: import java.io.ObjectInputStream;
036: import java.io.ObjectOutputStream;
037: import java.io.Serializable;
038: import java.net.URL;
039: import java.util.ArrayList;
040: import java.util.Collection;
041: import java.util.Collections;
042: import java.util.HashMap;
043: import java.util.Iterator;
044: import java.util.List;
045: import java.util.Map;
046: import java.util.Set;
047:
048: /**
049: * BeanContext implementation for Beehive Controls.
050: */
051: public class ControlBeanContextSupport extends
052: ControlBeanContextChildSupport implements BeanContext,
053: Serializable, PropertyChangeListener, VetoableChangeListener {
054:
055: private static final long serialVersionUID = 1L;
056:
057: private transient Map<Object, BCChild> _children;
058: private transient List<BeanContextMembershipListener> _bcMembershipListeners;
059:
060: // change listeners used for children, in an attempt to prevent
061: // the unintentional serialization of this bean context by a
062: // problematic child.
063: private transient PropertyChangeListener _childPcl;
064: private transient VetoableChangeListener _childVcl;
065:
066: private boolean _designTime = false;
067: private boolean _mayUseGui = false;
068:
069: /**
070: * Constructor.
071: */
072: public ControlBeanContextSupport() {
073: super ();
074: initialize();
075: }
076:
077: /**
078: * Constructor.
079: *
080: * @param peer
081: */
082: public ControlBeanContextSupport(BeanContext peer) {
083: super (peer);
084: initialize();
085: }
086:
087: /**
088: * Instantiate the javaBean named as a
089: * child of this <code>BeanContext</code>.
090: * The implementation of the JavaBean is
091: * derived from the value of the beanName parameter,
092: * and is defined by the
093: * <code>java.beans.Beans.instantiate()</code> method.
094: *
095: * @param beanName The name of the JavaBean to instantiate
096: * as a child of this <code>BeanContext</code>
097: * @throws IOException
098: * @throws ClassNotFoundException if the class identified
099: * by the beanName parameter is not found
100: */
101: public Object instantiateChild(String beanName) throws IOException,
102: ClassNotFoundException {
103: BeanContextChild bcc = getPeer();
104: return Beans.instantiate(bcc.getClass().getClassLoader(),
105: beanName, (BeanContext) bcc);
106: }
107:
108: /**
109: * Analagous to <code>java.lang.ClassLoader.getResourceAsStream()</code>,
110: * this method allows a <code>BeanContext</code> implementation
111: * to interpose behavior between the child <code>Component</code>
112: * and underlying <code>ClassLoader</code>.
113: *
114: * @param name the resource name
115: * @param bcc the specified child
116: * @return an <code>InputStream</code> for reading the resource,
117: * or <code>null</code> if the resource could not
118: * be found.
119: * @throws IllegalArgumentException if the resource is not valid
120: */
121: public InputStream getResourceAsStream(String name,
122: BeanContextChild bcc) throws IllegalArgumentException {
123:
124: // bcc must be a child of this context
125: if (!contains(bcc)) {
126: throw new IllegalArgumentException(
127: "Child is not a member of this context");
128: }
129:
130: ClassLoader cl = bcc.getClass().getClassLoader();
131: InputStream is;
132: if (cl != null && (is = cl.getResourceAsStream(name)) != null) {
133: return is;
134: }
135: return ClassLoader.getSystemResourceAsStream(name);
136: }
137:
138: /**
139: * Analagous to <code>java.lang.ClassLoader.getResource()</code>, this
140: * method allows a <code>BeanContext</code> implementation to interpose
141: * behavior between the child <code>Component</code>
142: * and underlying <code>ClassLoader</code>.
143: *
144: * @param name the resource name
145: * @param bcc the specified child
146: * @return a <code>URL</code> for the named
147: * resource for the specified child
148: * @throws IllegalArgumentException if the resource is not valid
149: */
150: public URL getResource(String name, BeanContextChild bcc)
151: throws IllegalArgumentException {
152:
153: // bcc must be a child of this context
154: if (!contains(bcc)) {
155: throw new IllegalArgumentException(
156: "Child is not a member of this context");
157: }
158:
159: ClassLoader cl = bcc.getClass().getClassLoader();
160: URL url;
161: if (cl != null && (url = cl.getResource(name)) != null) {
162: return url;
163: }
164: return ClassLoader.getSystemResource(name);
165: }
166:
167: /**
168: * Adds the specified <code>BeanContextMembershipListener</code>
169: * to receive <code>BeanContextMembershipEvents</code> from
170: * this <code>BeanContext</code> whenever it adds
171: * or removes a child <code>Component</code>(s).
172: *
173: * @param bcml the <code>BeanContextMembershipListener</code> to be added
174: */
175: public void addBeanContextMembershipListener(
176: BeanContextMembershipListener bcml) {
177: _bcMembershipListeners.add(bcml);
178: }
179:
180: /**
181: * Removes the specified <code>BeanContextMembershipListener</code>
182: * so that it no longer receives <code>BeanContextMembershipEvent</code>s
183: * when the child <code>Component</code>(s) are added or removed.
184: *
185: * @param bcml the <code>BeanContextMembershipListener</code>
186: * to be removed
187: */
188: public void removeBeanContextMembershipListener(
189: BeanContextMembershipListener bcml) {
190: _bcMembershipListeners.remove(bcml);
191: }
192:
193: /**
194: * Returns the number of children in this BeanContext. If this BeanContext
195: * contains more than <tt>Integer.MAX_VALUE</tt> children, returns
196: * <tt>Integer.MAX_VALUE</tt>.
197: *
198: * @return the number of elements in this collection
199: */
200: public int size() {
201: return _children.size();
202: }
203:
204: /**
205: * Returns <tt>true</tt> if this BeanContext has no children.
206: */
207: public boolean isEmpty() {
208: return _children.isEmpty();
209: }
210:
211: /**
212: * Returns true if this BeanContext contains the specified child.
213: *
214: * @param o element whose presence in this BeanContext is to be tested.
215: * @return true if this BeanContext contains the specified child
216: * @throws ClassCastException if the type of the specified element
217: * is incompatible with this collection (optional).
218: * @throws NullPointerException if the specified element is null and this
219: * collection does not support null elements (optional).
220: */
221: public boolean contains(Object o) {
222: return _children.containsKey(o);
223: }
224:
225: /**
226: * Returns an iterator over the elements in this collection. The
227: * iterator's collection is non-modifiable and element does not
228: * correspond to the order that children are added.
229: *
230: * @return an <tt>Iterator</tt> over the children of this BeanContext
231: */
232: public Iterator iterator() {
233: return Collections.unmodifiableSet(_children.keySet())
234: .iterator();
235: }
236:
237: /**
238: * Returns an array containing all of the children in this BeanContext.
239: * <p/>
240: * The returned array will be "safe" in that no references to it are
241: * maintained by this collection. (In other words, this method must
242: * allocate a new array even if this collection is backed by an array).
243: * The caller is thus free to modify the returned array.<p>
244: * <p/>
245: * This method acts as bridge between array-based and collection-based
246: * APIs.
247: *
248: * @return an array containing all of the elements in this collection
249: */
250: public Object[] toArray() {
251: return _children.keySet().toArray();
252: }
253:
254: /**
255: * Add a child to this BeanContext. If the child is already a child
256: * of this bean context this method returns immediately with a return
257: * value of false.
258: * <p/>
259: * If the child implements the BeanContextProxy interface, the child
260: * AND the BeanContextChild referenced by the proxy are added to this
261: * BeanContext.
262: *
263: * @param o element whose presence in this collection is to be ensured.
264: * @return <tt>true</tt> if this collection changed as a result of the
265: * call
266: * @throws UnsupportedOperationException <tt>add</tt> is not supported by
267: * this collection.
268: * @throws ClassCastException class of the specified element prevents it
269: * from being added to this collection.
270: * @throws NullPointerException if the specified element is null and this
271: * collection does not support null elements.
272: * @throws IllegalArgumentException some aspect of this element prevents
273: * it from being added to this collection.
274: */
275: public boolean add(Object o) {
276: return internalAdd(o, true);
277: }
278:
279: /**
280: * Remove the specified child from this BeanContext. If the child
281: * to be removed implements the BeanContextProxy interface or is
282: * referenced from an existing BeanContextProxy child both children
283: * will be removed.
284: *
285: * @param o element to be removed from this collection, if present.
286: * @return <tt>true</tt> if this collection changed as a result of the
287: * call
288: * @throws ClassCastException if the type of the specified element
289: * is incompatible with this collection (optional).
290: * @throws NullPointerException if the specified element is null and this
291: * collection does not support null elements (optional).
292: * @throws UnsupportedOperationException remove is not supported by this
293: * collection.
294: */
295: public boolean remove(Object o) {
296: return internalRemove(o, true);
297: }
298:
299: /**
300: * Not supported.
301: *
302: * @throws UnsupportedOperationException
303: */
304: public boolean addAll(Collection c) {
305: // NOOP : Not Supported
306: throw new UnsupportedOperationException();
307: }
308:
309: /**
310: * Not supported.
311: *
312: * @throws UnsupportedOperationException
313: */
314: public void clear() {
315: // NOOP: Not supported
316: throw new UnsupportedOperationException();
317: }
318:
319: /**
320: * Not supported.
321: *
322: * @throws UnsupportedOperationException
323: */
324: public boolean retainAll(Collection c) {
325: // NOOP: Not supported
326: throw new UnsupportedOperationException();
327: }
328:
329: /**
330: * Not supported.
331: *
332: * @throws UnsupportedOperationException
333: */
334: public boolean removeAll(Collection c) {
335: throw new UnsupportedOperationException();
336: }
337:
338: /**
339: * Returns <tt>true</tt> if this BeanContext contains all of the children
340: * in the specified collection.
341: *
342: * @param c collection to be checked for containment in this collection.
343: * @return <tt>true</tt> if this collection contains all of the elements
344: * in the specified collection
345: * @throws ClassCastException if the types of one or more elements
346: * in the specified collection are incompatible with this
347: * collection (optional).
348: * @throws NullPointerException if the specified collection contains one
349: * or more null elements and this collection does not support null
350: * elements (optional).
351: * @throws NullPointerException if the specified collection is
352: * <tt>null</tt>.
353: * @see #contains(Object)
354: */
355: public boolean containsAll(Collection c) {
356: return _children.keySet().containsAll(c);
357: }
358:
359: /**
360: * Returns an array containing all of the children of this BeanContext;
361: * the runtime type of the returned array is that of the specified array.
362: *
363: * @param a the array into which the elements of this collection are to be
364: * stored, if it is big enough; otherwise, a new array of the same
365: * runtime type is allocated for this purpose.
366: * @return an array containing the elements of this collection
367: * @throws ArrayStoreException the runtime type of the specified array is
368: * not a supertype of the runtime type of every element in this
369: * collection.
370: * @throws NullPointerException if the specified array is <tt>null</tt>.
371: */
372: public Object[] toArray(Object[] a) {
373: return _children.keySet().toArray(a);
374: }
375:
376: /**
377: * Sets the "value" of the "designTime" property.
378: * <p/>
379: * If the implementing object is an instance of java.beans.beancontext.BeanContext,
380: * or a subinterface thereof, then that BeanContext should fire a
381: * PropertyChangeEvent, to its registered BeanContextMembershipListeners, with
382: * parameters:
383: * <ul>
384: * <li><code>propertyName</code> - <code>java.beans.DesignMode.PROPERTYNAME</code>
385: * <li><code>oldValue</code> - previous value of "designTime"
386: * <li><code>newValue</code> - current value of "designTime"
387: * </ul>
388: * Note it is illegal for a BeanContextChild to invoke this method
389: * associated with a BeanContext that it is nested within.
390: *
391: * @param designTime the current "value" of the "designTime" property
392: * @see java.beans.beancontext.BeanContext
393: * @see java.beans.beancontext.BeanContextMembershipListener
394: * @see java.beans.PropertyChangeEvent
395: */
396: public void setDesignTime(boolean designTime) {
397: if (designTime == _designTime)
398: return;
399: _designTime = designTime;
400: firePropertyChange("designTime", !_designTime, designTime);
401: }
402:
403: /**
404: * A value of true denotes that JavaBeans should behave in design time
405: * mode, a value of false denotes runtime behavior.
406: *
407: * @return the current "value" of the "designTime" property.
408: */
409: public boolean isDesignTime() {
410: return _designTime;
411: }
412:
413: /**
414: * Determines whether this bean needs a GUI.
415: *
416: * @return True if the bean absolutely needs a GUI available in
417: * order to get its work done.
418: */
419: public boolean needsGui() {
420: BeanContextChild bcc = getPeer();
421: if (bcc != this && bcc instanceof Visibility) {
422: return ((Visibility) bcc).needsGui();
423: }
424:
425: // check children
426: for (Object o : _children.keySet()) {
427: if (o instanceof Visibility) {
428: if (((Visibility) o).needsGui()) {
429: return true;
430: }
431: }
432: }
433: return false;
434: }
435:
436: /**
437: * This method instructs the bean that it should not use the Gui.
438: */
439: public void dontUseGui() {
440: if (!_mayUseGui)
441: return;
442:
443: _mayUseGui = false;
444:
445: for (Object o : _children.keySet()) {
446: if (o instanceof Visibility) {
447: ((Visibility) o).dontUseGui();
448: }
449: }
450: }
451:
452: /**
453: * This method instructs the bean that it is OK to use the Gui.
454: */
455: public void okToUseGui() {
456: if (_mayUseGui)
457: return;
458:
459: _mayUseGui = true;
460:
461: for (Object o : _children.keySet()) {
462: if (o instanceof Visibility) {
463: ((Visibility) o).okToUseGui();
464: }
465: }
466: }
467:
468: /**
469: * Determines whether this bean is avoiding using a GUI.
470: *
471: * @return true if the bean is currently avoiding use of the Gui.
472: * e.g. due to a call on dontUseGui().
473: */
474: public boolean avoidingGui() {
475: return !_mayUseGui && needsGui();
476: }
477:
478: /**
479: * This method gets called when a bound property is changed.
480: *
481: * @param evt A PropertyChangeEvent object describing the event source
482: * and the property that has changed.
483: */
484: public void propertyChange(PropertyChangeEvent evt) {
485: // monitor "beanContext" property
486: if ("beanContext".equals(evt.getPropertyName())
487: && contains(evt.getSource())) {
488: BeanContext bc = (BeanContext) getPeer();
489: if (bc.equals(evt.getOldValue())
490: && !bc.equals(evt.getNewValue())) {
491: internalRemove(evt.getSource(), false);
492: }
493: }
494: }
495:
496: /**
497: * This method gets called when a constrained property is changed.
498: *
499: * @param evt a <code>PropertyChangeEvent</code> object describing the
500: * event source and the property that has changed.
501: * @throws java.beans.PropertyVetoException
502: * if the recipient wishes the property
503: * change to be rolled back.
504: */
505: public void vetoableChange(PropertyChangeEvent evt)
506: throws PropertyVetoException {
507: // monitor "beanContext" property
508: if ("beanContext".equals(evt.getPropertyName())
509: && contains(evt.getOldValue())) {
510: // noop: at this point doesn't veto
511: }
512: }
513:
514: /**
515: * *************************************************************************************
516: */
517:
518: /**
519: * Init this classes data structures.
520: */
521: protected void initialize() {
522: _bcMembershipListeners = new ArrayList<BeanContextMembershipListener>();
523: _children = Collections
524: .synchronizedMap(new HashMap<Object, BCChild>());
525:
526: _childPcl = new PropertyChangeListener() {
527: public void propertyChange(PropertyChangeEvent pce) {
528: ControlBeanContextSupport.this .propertyChange(pce);
529: }
530: };
531:
532: _childVcl = new VetoableChangeListener() {
533: public void vetoableChange(PropertyChangeEvent pce)
534: throws PropertyVetoException {
535: ControlBeanContextSupport.this .vetoableChange(pce);
536: }
537: };
538: }
539:
540: /**
541: * Fire a BeanContextMembershipEvent.
542: *
543: * @param bcme Event to fire.
544: * @param childrenAdded True if add event, false if remove event.
545: */
546: private void fireMembershipEvent(BeanContextMembershipEvent bcme,
547: boolean childrenAdded) {
548:
549: for (BeanContextMembershipListener bcml : _bcMembershipListeners) {
550: if (childrenAdded) {
551: bcml.childrenAdded(bcme);
552: } else {
553: bcml.childrenRemoved(bcme);
554: }
555: }
556: }
557:
558: /**
559: * The internalAdd method is used in two different cases. If an add is done
560: * through the public add() api, this method is invoked with the publicApi
561: * parameter set to true. During deserialization of children this method
562: * is invoked with publicApi set to false. During deserialization it is
563: * not necessary to set Visibility features or re-register as a listener.
564: *
565: * @param o
566: * @param publicApi
567: * @return true if added.
568: */
569: private boolean internalAdd(Object o, boolean publicApi) {
570:
571: if (contains(o))
572: return false;
573:
574: // todo: for multithreaded usage this block needs to be synchronized
575: // spec: if the object being added implements BeanContextChild or BeanContextProxy
576: // need to set the bean context of the object to this bean context.
577: BeanContextChild bcc = null;
578: BeanContextProxy bcp = null;
579:
580: if (o instanceof BeanContextProxy) {
581: if (o instanceof BeanContext) {
582: throw new IllegalArgumentException(
583: "May not implement both BeanContextProxy and BeanContext!!");
584: }
585: bcp = (BeanContextProxy) o;
586: bcc = bcp.getBeanContextProxy();
587: } else if (o instanceof BeanContextChild) {
588: bcc = (BeanContextChild) o;
589: }
590:
591: if (bcc != null) {
592: try {
593: bcc.setBeanContext((BeanContext) getPeer());
594: } catch (PropertyVetoException e) {
595: throw new IllegalStateException(e);
596: }
597:
598: bcc.addPropertyChangeListener("beanContext", _childPcl);
599: bcc.addVetoableChangeListener("beanContext", _childVcl);
600: }
601:
602: if (publicApi) {
603: if (o instanceof Visibility) {
604: if (_mayUseGui) {
605: ((Visibility) o).okToUseGui();
606: } else {
607: ((Visibility) o).dontUseGui();
608: }
609: }
610: if (o instanceof BeanContextMembershipListener) {
611: addBeanContextMembershipListener((BeanContextMembershipListener) o);
612: }
613: }
614:
615: if (bcp == null) {
616: _children.put(o, new BCChild(o));
617: } else {
618: _children.put(bcp, new BCChild(bcp, bcc, true));
619: _children.put(bcc, new BCChild(bcp, bcc, false));
620: }
621:
622: BeanContextMembershipEvent bcme = new BeanContextMembershipEvent(
623: (BeanContext) getPeer(), new Object[] { o });
624: fireMembershipEvent(bcme, true);
625: // }
626: return true;
627: }
628:
629: /**
630: * There are two ways a object can be removed from a BeanContext, by either explicitly invoking the
631: * remove() api or if the child implements BeanContextChild by calling its setBeanContext() api.
632: *
633: * @param o
634: * @param publicApi
635: * @return true if successful
636: */
637: private boolean internalRemove(Object o, boolean publicApi) {
638:
639: if (!contains(o))
640: return false;
641:
642: // todo: for multithreaded usage this block needs to be synchronized
643: BeanContextChild bcc = null;
644: if (o instanceof BeanContextProxy) {
645: bcc = ((BeanContextProxy) o).getBeanContextProxy();
646: } else if (o instanceof BeanContextChild) {
647: bcc = (BeanContextChild) o;
648: }
649:
650: if (bcc != null) {
651:
652: /*
653: If remove invoked as a result of the BeanContext receiving an unexpected PropertyChangeEvent
654: notification as a result of a 3rd party invoking setBeanContext() then the remove implementation
655: shall not invoke setBeanContext(null) on that child as part of the remove() semantics, since
656: doing so would overwrite the value previously set by the 3rd party.
657: */
658: if (publicApi) {
659:
660: // remove the property/veto listeners -- we know we want to remove the bean
661: // and don't need to be notified if we have initiated the removal
662: bcc.removePropertyChangeListener("beanContext",
663: _childPcl);
664: bcc.removeVetoableChangeListener("beanContext",
665: _childVcl);
666:
667: try {
668: bcc.setBeanContext(null);
669: } catch (PropertyVetoException e) {
670: // rewire the listeners we removed above then except
671: bcc.addPropertyChangeListener("beanContext",
672: _childPcl);
673: bcc.addVetoableChangeListener("beanContext",
674: _childVcl);
675: throw new IllegalStateException(e);
676: }
677: }
678: }
679:
680: if (o instanceof BeanContextMembershipListener) {
681: removeBeanContextMembershipListener((BeanContextMembershipListener) o);
682: }
683:
684: BCChild bc = _children.get(o);
685: if (bc.isProxy()) {
686: _children.remove(bc.getChild());
687: } else if (bc.hasProxy()) {
688: _children.remove(bc.getProxy());
689: }
690: _children.remove(o);
691:
692: BeanContextMembershipEvent bcme = new BeanContextMembershipEvent(
693: (BeanContext) getPeer(), new Object[] { o });
694: fireMembershipEvent(bcme, false);
695: // end synchronized block
696: return true;
697: }
698:
699: /**
700: * Serialize all serializable children (unless this BeanContext has a peer). Any
701: * children which are not serializable not be present upon deserialization. Also
702: * serialize any BeanContextMembership listeners which are serializable.
703: *
704: * @param out ObjectOutputStream to serialize to.
705: */
706: private synchronized void writeObject(ObjectOutputStream out)
707: throws IOException {
708:
709: // todo: for multithreaded usage this block needs to be synchronized
710: out.defaultWriteObject();
711:
712: // spec: only write children if not using a peer
713: if (this .equals(getPeer())) {
714: writeChildren(out);
715: } else {
716: out.writeInt(0);
717: }
718:
719: // write event handlers
720: int serializable = 0;
721: for (BeanContextMembershipListener listener : _bcMembershipListeners) {
722: if (listener instanceof Serializable)
723: serializable++;
724: }
725:
726: out.writeInt(serializable);
727: if (serializable > 0) {
728: for (BeanContextMembershipListener listener : _bcMembershipListeners) {
729: if (listener instanceof Serializable) {
730: out.writeObject(listener);
731: }
732: }
733: }
734: // end synchronized block
735: }
736:
737: /**
738: * Necessary for the case of this bean context having a peer. The specification
739: * states that a bean context which has a peer should not serialize its children,
740: * this hook is necessary to allow the peer to serialize children.
741: *
742: * @param oos ObjectOutputStream
743: * @throws IOException
744: */
745: public final void writeChildren(ObjectOutputStream oos)
746: throws IOException {
747: int serializable = 0;
748: Set<Map.Entry<Object, BCChild>> bcChildren = _children
749: .entrySet();
750: for (Map.Entry<Object, BCChild> entry : bcChildren) {
751: if (entry.getValue().isSerializable()) {
752: serializable++;
753: }
754: }
755:
756: oos.writeInt(serializable);
757: if (serializable > 0) {
758: for (Map.Entry<Object, BCChild> bc : bcChildren) {
759: if (bc.getValue().isSerializable()) {
760: oos.writeObject(bc.getKey());
761: }
762: }
763: }
764: }
765:
766: /**
767: * Deserialize this an instance of this class, including any children and
768: * BeanContextMembershipListeners which were present during serialization and
769: * were serializable.
770: *
771: * @param in ObjectInputStream to deserialize from.
772: * @throws IOException
773: * @throws ClassNotFoundException
774: */
775: private synchronized void readObject(ObjectInputStream in)
776: throws IOException, ClassNotFoundException {
777: // todo: for multithreaded usage this block needs to be synchronized
778: in.defaultReadObject();
779: initialize();
780:
781: // only deserialize child if not using a peer
782: if (this .equals(getPeer())) {
783: readChildren(in);
784: }
785:
786: int listenerCount = in.readInt();
787: for (int i = 0; i < listenerCount; i++) {
788: addBeanContextMembershipListener((BeanContextMembershipListener) in
789: .readObject());
790: }
791: // end synchronized block
792: }
793:
794: /**
795: * This public api is necessary to allow a bean context with a peer to deserialize its children.
796: * This api is not part any standard api.
797: *
798: * @param in ObjectInputStream
799: * @throws IOException
800: * @throws ClassNotFoundException
801: */
802: public final void readChildren(ObjectInputStream in)
803: throws IOException, ClassNotFoundException {
804: int childCount = in.readInt();
805: for (int i = 0; i < childCount; i++) {
806: internalAdd(in.readObject(), false);
807: }
808: }
809:
810: /**
811: * A child of this BeanContext. This class is used to manage the relationship
812: * between a BeanContextProxy and its BeanContextChild. When a BeanContextProxy
813: * is added or removed from this context the BeanContextChild it references must
814: * also be added or removed. This requires that both the BeanContextChild and
815: * BeanContextProxy are added/removed to the list of children. This class
816: * is used to map from the proxy -> child and child -> proxy.
817: * <p/>
818: * The _child field is always guarenteed to be non-null, the proxy field may
819: * be null if this child does not have a proxy.
820: */
821: private final static class BCChild {
822:
823: private Object _child;
824: private BeanContextProxy _proxy;
825: private boolean _isProxy;
826: private boolean _serializable;
827:
828: /**
829: * Construct a new BCChild for a child which is not related to a BeanContextProxy.
830: *
831: * @param child child to add -- must not be an instance of BeanContextProxy.
832: */
833: protected BCChild(Object child) {
834: assert child != null;
835: assert !(child instanceof BeanContextProxy);
836:
837: _child = child;
838: _proxy = null;
839: _isProxy = false;
840: _serializable = _child instanceof Serializable;
841: }
842:
843: /**
844: * Construct a new BCChild for a proxy -> child relationship.
845: *
846: * @param proxy BeanContextProxy
847: * @param child BeanContextChild
848: * @param isProxy true if this will be entered into the child map keyed on the proxy.
849: */
850: protected BCChild(BeanContextProxy proxy,
851: BeanContextChild child, boolean isProxy) {
852: assert child != null;
853: assert proxy != null;
854:
855: _child = child;
856: _proxy = proxy;
857: _isProxy = isProxy;
858: _serializable = (_isProxy)
859: && _child instanceof Serializable
860: && _proxy instanceof Serializable;
861: }
862:
863: /**
864: * Get the proxy.
865: */
866: protected BeanContextProxy getProxy() {
867: return _proxy;
868: }
869:
870: /**
871: * Get the child.
872: */
873: protected Object getChild() {
874: return _child;
875: }
876:
877: /**
878: * True if a proxy was set for this child.
879: */
880: protected boolean hasProxy() {
881: return _proxy != null;
882: }
883:
884: /**
885: * True if this child was keyed by its proxy in the child map.
886: */
887: protected boolean isProxy() {
888: return _isProxy;
889: }
890:
891: /**
892: * True if this BCChild is serializable.
893: */
894: protected boolean isSerializable() {
895: return _serializable;
896: }
897: }
898: }
|