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.beancontext.BeanContext;
023: import java.beans.beancontext.BeanContextChild;
024: import java.beans.beancontext.BeanContextServiceAvailableEvent;
025: import java.beans.beancontext.BeanContextServiceProvider;
026: import java.beans.beancontext.BeanContextServiceRevokedEvent;
027: import java.beans.beancontext.BeanContextServiceRevokedListener;
028: import java.beans.beancontext.BeanContextServices;
029: import java.beans.beancontext.BeanContextServicesListener;
030: import java.io.IOException;
031: import java.io.ObjectInputStream;
032: import java.io.ObjectOutputStream;
033: import java.io.Serializable;
034: import java.util.ArrayList;
035: import java.util.Collections;
036: import java.util.HashMap;
037: import java.util.Iterator;
038: import java.util.List;
039: import java.util.Map;
040: import java.util.Set;
041: import java.util.TooManyListenersException;
042:
043: /**
044: * Implementation of BeanContextServices for Beehive Controls. Assumes single threaded usage.
045: */
046: public class ControlBeanContextServicesSupport extends
047: ControlBeanContextSupport implements BeanContextServices {
048:
049: private transient Map<Class/*service class*/, ServiceProvider> _serviceProviders;
050: private transient List<BeanContextServicesListener> _bcsListeners;
051:
052: /**
053: * Constructor.
054: */
055: public ControlBeanContextServicesSupport() {
056: super ();
057: }
058:
059: /**
060: * Constructor which allows delegate to be passed in.
061: *
062: * @param peer BeanContextServices peer.
063: */
064: public ControlBeanContextServicesSupport(BeanContextServices peer) {
065: super (peer);
066: }
067:
068: /**
069: * Adds a service to this BeanContext.
070: * <code>BeanContextServiceProvider</code>s call this method
071: * to register a particular service with this context.
072: * If the service has not previously been added, the
073: * <code>BeanContextServices</code> associates
074: * the service with the <code>BeanContextServiceProvider</code> and
075: * fires a <code>BeanContextServiceAvailableEvent</code> to all
076: * currently registered <code>BeanContextServicesListeners</code>.
077: * The method then returns <code>true</code>, indicating that
078: * the addition of the service was successful.
079: * If the given service has already been added, this method
080: * simply returns <code>false</code>.
081: *
082: * @param serviceClass the service to add
083: * @param serviceProvider the <code>BeanContextServiceProvider</code>
084: * associated with the service
085: * @return true if service was added.
086: */
087: public boolean addService(Class serviceClass,
088: BeanContextServiceProvider serviceProvider) {
089: // todo: for multithreaded usage this block needs to be synchronized
090: if (!_serviceProviders.containsKey(serviceClass)) {
091: _serviceProviders.put(serviceClass, new ServiceProvider(
092: serviceProvider));
093: BeanContextServiceAvailableEvent bcsae = new BeanContextServiceAvailableEvent(
094: (BeanContextServices) getPeer(), serviceClass);
095: fireServiceAvailableEvent(bcsae);
096: return true;
097: }
098: // end synchronized
099: return false;
100: }
101:
102: /**
103: * BeanContextServiceProviders wishing to remove
104: * a currently registered service from this context
105: * may do so via invocation of this method. Upon revocation of
106: * the service, the <code>BeanContextServices</code> fires a
107: * <code>BeanContextServiceRevokedEvent</code> to its
108: * list of currently registered
109: * <code>BeanContextServiceRevokedListeners</code> and
110: * <code>BeanContextServicesListeners</code>.
111: *
112: * @param serviceClass the service to revoke from this BeanContextServices
113: * @param serviceProvider the BeanContextServiceProvider associated with
114: * this particular service that is being revoked
115: * @param revokeCurrentServicesNow a value of <code>true</code>
116: * indicates an exceptional circumstance where the
117: * <code>BeanContextServiceProvider</code> or
118: * <code>BeanContextServices</code> wishes to immediately
119: * terminate service to all currently outstanding references
120: * to the specified service.
121: */
122: public void revokeService(Class serviceClass,
123: BeanContextServiceProvider serviceProvider,
124: boolean revokeCurrentServicesNow) {
125: // todo: for multithreaded usage this block needs to be synchronized
126: if (!_serviceProviders.containsKey(serviceClass)) {
127: return;
128: }
129:
130: // propagate to any children implementing BeanContextServices
131: Iterator i = iterator();
132: while (i.hasNext()) {
133: Object o = i.next();
134: if (o instanceof BeanContextServices) {
135: ((BeanContextServices) o).revokeService(serviceClass,
136: serviceProvider, revokeCurrentServicesNow);
137: }
138: }
139:
140: BeanContextServiceRevokedEvent bcsre = new BeanContextServiceRevokedEvent(
141: (BeanContextServices) getPeer(), serviceClass,
142: revokeCurrentServicesNow);
143: fireServiceRevokedEvent(bcsre);
144:
145: // fire revoked event to requestor listeners, if the service is delegated the owner of the
146: // service should fire these events.
147: ServiceProvider sp = _serviceProviders.get(serviceClass);
148: if (!sp.isDelegated()) {
149: sp.revoke(bcsre);
150: }
151:
152: if (revokeCurrentServicesNow || !sp.hasRequestors()) {
153: _serviceProviders.remove(serviceClass);
154: }
155: // end synchronized
156: }
157:
158: /**
159: * Reports whether or not a given service is
160: * currently available from this context.
161: *
162: * @param serviceClass the service in question
163: * @return true if the service is available
164: */
165: public synchronized boolean hasService(Class serviceClass) {
166:
167: // todo: for multithreaded usage this block needs to be synchronized
168: ServiceProvider sp = _serviceProviders.get(serviceClass);
169: if (sp != null && !sp.isRevoked()) {
170: return true;
171: }
172:
173: // if service not found locally check in nested context
174: BeanContext bc = getBeanContext();
175: if (bc instanceof BeanContextServices) {
176: return ((BeanContextServices) bc).hasService(serviceClass);
177: }
178: return false;
179: // end synchronized
180: }
181:
182: /**
183: * A <code>BeanContextChild</code>, or any arbitrary object
184: * associated with a <code>BeanContextChild</code>, may obtain
185: * a reference to a currently registered service from its
186: * nesting <code>BeanContextServices</code>
187: * via invocation of this method. When invoked, this method
188: * gets the service by calling the getService() method on the
189: * underlying <code>BeanContextServiceProvider</code>.
190: *
191: * @param child the <code>BeanContextChild</code>
192: * associated with this request
193: * @param requestor the object requesting the service
194: * @param serviceClass class of the requested service
195: * @param serviceSelector the service dependent parameter
196: * @param bcsrl the
197: * <code>BeanContextServiceRevokedListener</code> to notify
198: * if the service should later become revoked
199: * @return a reference to this context's named
200: * Service as requested or <code>null</code>
201: * @throws java.util.TooManyListenersException
202: *
203: */
204: public Object getService(BeanContextChild child, Object requestor,
205: Class serviceClass, Object serviceSelector,
206: BeanContextServiceRevokedListener bcsrl)
207: throws TooManyListenersException {
208:
209: if (!contains(child)) {
210: throw new IllegalArgumentException(child
211: + "is not a child of this context!");
212: }
213:
214: Object service;
215: BeanContext bc = getBeanContext();
216:
217: // todo: for multithreaded usage this block needs to be synchronized
218: // check to see if this is a known service
219: ServiceProvider sp = _serviceProviders.get(serviceClass);
220: if (sp != null) {
221: if (sp.isRevoked()) {
222: return null;
223: } else if (sp.isDelegated()) {
224: service = ((BeanContextServices) bc).getService(
225: getPeer(), requestor, serviceClass,
226: serviceSelector, bcsrl);
227: } else {
228: service = sp.getBCServiceProvider().getService(
229: (BeanContextServices) getPeer(), requestor,
230: serviceClass, serviceSelector);
231: }
232:
233: if (service != null) {
234: sp.addChildReference(requestor, bcsrl);
235: }
236: return service;
237: }
238:
239: // unknown service provider, delegate the request to the nested BeanContextServices (if available)
240: if (bc instanceof BeanContextServices) {
241: service = ((BeanContextServices) bc).getService(getPeer(),
242: requestor, serviceClass, serviceSelector, bcsrl);
243: if (service != null) {
244: sp = new ServiceProvider();
245: sp.addChildReference(requestor, bcsrl);
246: _serviceProviders.put(serviceClass, sp);
247: }
248: return service;
249: }
250: return null;
251: }
252:
253: /**
254: * Releases a <code>BeanContextChild</code>'s
255: * (or any arbitrary object associated with a BeanContextChild)
256: * reference to the specified service by calling releaseService()
257: * on the underlying <code>BeanContextServiceProvider</code>.
258: *
259: * @param child the <code>BeanContextChild</code>
260: * @param requestor the requestor
261: * @param service the service
262: */
263: public void releaseService(BeanContextChild child,
264: Object requestor, Object service) {
265:
266: if (!contains(child)) {
267: throw new IllegalArgumentException(child
268: + "is not a child of this context!");
269: }
270:
271: // todo: for multithreaded usage this block needs to be synchronized
272: Class serviceClass = findServiceClass(service);
273: ServiceProvider sp = _serviceProviders.get(serviceClass);
274: sp.removeChildReference(requestor);
275:
276: // if this is a delegated service, delegate the release request
277: // delegated services are removed from the _serviceProviders table
278: // as soon as their reference count drops to zero
279: if (sp.isDelegated()) {
280: BeanContextServices bcs = (BeanContextServices) getBeanContext();
281: bcs.releaseService(this , requestor, service);
282: if (!sp.hasRequestors()) {
283: _serviceProviders.remove(serviceClass);
284: }
285: } else {
286: sp.getBCServiceProvider()
287: .releaseService((BeanContextServices) getPeer(),
288: requestor, service);
289: }
290: // end synchronized
291: }
292:
293: /**
294: * Gets the currently available services for this context.
295: *
296: * @return an <code>Iterator</code> consisting of the
297: * currently available services
298: */
299: public Iterator getCurrentServiceClasses() {
300:
301: ArrayList<Class> currentClasses = new ArrayList<Class>();
302: for (Class serviceClass : _serviceProviders.keySet()) {
303: ServiceProvider sp = _serviceProviders.get(serviceClass);
304: if (!sp.isRevoked() && !sp.isDelegated()) {
305: currentClasses.add(serviceClass);
306: }
307: }
308: return currentClasses.iterator();
309: }
310:
311: /**
312: * Gets the list of service dependent service parameters
313: * (Service Selectors) for the specified service, by
314: * calling getCurrentServiceSelectors() on the
315: * underlying BeanContextServiceProvider.
316: *
317: * @param serviceClass the specified service
318: * @return the currently available service selectors
319: * for the named serviceClass
320: */
321: public Iterator getCurrentServiceSelectors(Class serviceClass) {
322:
323: if (_serviceProviders.containsKey(serviceClass)) {
324: ServiceProvider sp = _serviceProviders.get(serviceClass);
325: if (!sp.isDelegated() && !sp.isRevoked()) {
326: return sp.getBCServiceProvider()
327: .getCurrentServiceSelectors(
328: (BeanContextServices) getPeer(),
329: serviceClass);
330: }
331: }
332: return null;
333: }
334:
335: /**
336: * Adds a <code>BeanContextServicesListener</code> to this BeanContext.
337: *
338: * @param bcsl the <code>BeanContextServicesListener</code> to add
339: */
340: public void addBeanContextServicesListener(
341: BeanContextServicesListener bcsl) {
342: if (!_bcsListeners.contains(bcsl)) {
343: _bcsListeners.add(bcsl);
344: }
345: }
346:
347: /**
348: * Removes a <code>BeanContextServicesListener</code>
349: * from this <code>BeanContext</code>.
350: *
351: * @param bcsl the <code>BeanContextServicesListener</code>
352: * to remove from this context
353: */
354: public void removeBeanContextServicesListener(
355: BeanContextServicesListener bcsl) {
356: _bcsListeners.remove(bcsl);
357: }
358:
359: /*
360: * **********************************************************************************
361: * Protected
362: * **********************************************************************************
363: */
364:
365: /**
366: * Invoked when all resources obtained from the current nested bean context
367: * need to be released. For BeanContextServices this means revoke any services
368: * obtained from a delegate services provider. Typically invoked when the parent
369: * context is changed.
370: */
371: protected synchronized void releaseBeanContextResources() {
372:
373: for (Class serviceClass : _serviceProviders.keySet()) {
374: ServiceProvider sp = _serviceProviders.get(serviceClass);
375: if (sp.isDelegated()) {
376: sp.revoke(new BeanContextServiceRevokedEvent(
377: (BeanContextServices) getPeer(), serviceClass,
378: true));
379: }
380: }
381: }
382:
383: /*
384: * **********************************************************************************
385: * Private
386: * **********************************************************************************
387: */
388:
389: /**
390: * first a service available event.
391: *
392: * @param bcsae
393: */
394: private void fireServiceAvailableEvent(
395: BeanContextServiceAvailableEvent bcsae) {
396: for (BeanContextServicesListener bcsl : _bcsListeners) {
397: bcsl.serviceAvailable(bcsae);
398: }
399: }
400:
401: /**
402: * Fire a service revoked event.
403: *
404: * @param bcsre
405: */
406: private void fireServiceRevokedEvent(
407: BeanContextServiceRevokedEvent bcsre) {
408: for (BeanContextServicesListener bcsl : _bcsListeners) {
409: bcsl.serviceRevoked(bcsre);
410: }
411: }
412:
413: /**
414: * Initialize data structures.
415: */
416: protected void initialize() {
417: super .initialize();
418: _serviceProviders = Collections
419: .synchronizedMap(new HashMap<Class, ServiceProvider>());
420: _bcsListeners = Collections
421: .synchronizedList(new ArrayList<BeanContextServicesListener>());
422: }
423:
424: /**
425: * Try to find the registered service for a service object instance.
426: * May return null if the object instance is not from a service registered
427: * with this service provider.
428: *
429: * @return Service class for service instance.
430: * @throws IllegalArgumentException if service class can not be found.
431: */
432: private Class findServiceClass(Object service) {
433: for (Class sc : _serviceProviders.keySet()) {
434: if (sc.isInstance(service)) {
435: return sc;
436: }
437: }
438: throw new IllegalArgumentException(
439: "Cannot find service provider for: "
440: + service.getClass().getCanonicalName());
441: }
442:
443: /**
444: * Deserialization support.
445: *
446: * @param ois
447: * @throws IOException
448: * @throws ClassNotFoundException
449: */
450: private synchronized void readObject(ObjectInputStream ois)
451: throws IOException, ClassNotFoundException {
452: ois.defaultReadObject();
453:
454: int svcsSize = ois.readInt();
455: for (int i = 0; i < svcsSize; i++) {
456: _serviceProviders.put((Class) ois.readObject(),
457: (ServiceProvider) ois.readObject());
458: }
459:
460: int listenersSize = ois.readInt();
461: for (int i = 0; i < listenersSize; i++) {
462: _bcsListeners.add((BeanContextServicesListener) ois
463: .readObject());
464: }
465: }
466:
467: /**
468: * Serialize this instance including any serializable services and BeanContextServicesListeners.
469: * Any services or listeners which are not Serializable will not be present once deserialized.
470: *
471: * @param oos
472: * @throws IOException
473: */
474: private synchronized void writeObject(ObjectOutputStream oos)
475: throws IOException {
476:
477: int serializable = 0;
478: oos.defaultWriteObject();
479:
480: // write out the service providers
481: Set<Map.Entry<Class, ServiceProvider>> providers = _serviceProviders
482: .entrySet();
483: for (Map.Entry<Class, ServiceProvider> provider : providers) {
484: if (provider.getValue().isSerializable()) {
485: serializable++;
486: }
487: }
488:
489: oos.writeInt(serializable);
490: if (serializable > 0) {
491: for (Map.Entry<Class, ServiceProvider> entry : providers) {
492: if (entry.getValue().isSerializable()) {
493: oos.writeObject(entry.getKey());
494: oos.writeObject(entry.getValue());
495: }
496: }
497: }
498:
499: // write out the event listeners
500: serializable = 0;
501: for (BeanContextServicesListener l : _bcsListeners) {
502: if (l instanceof Serializable) {
503: serializable++;
504: }
505: }
506:
507: oos.writeInt(serializable);
508: if (serializable > 0) {
509: for (BeanContextServicesListener l : _bcsListeners) {
510: if (l instanceof Serializable) {
511: oos.writeObject(l);
512: }
513: }
514: }
515: }
516:
517: /*
518: * ***************************************************************************************
519: * Inner Classes
520: * ***************************************************************************************
521: */
522:
523: /**
524: * A ServiceProvider instance is associated with a service class in a Map. One ServiceProvider
525: * exists for a given service type. The Service manager keeps track of all of the requestors
526: * for the given service. It provides functionality to add new references to services, remove
527: * references to services, and to notify referenants that a service has been revoked.
528: */
529: private static final class ServiceProvider implements Serializable {
530:
531: private transient HashMap<Object/*requestor*/, ChildServiceReference> _requestors;
532: private BeanContextServiceProvider _bcss;
533: private boolean _revoked;
534: private final boolean _serializable;
535:
536: /**
537: * Constructor for delegated service provider.
538: */
539: private ServiceProvider() {
540: _bcss = null;
541: _serializable = false;
542: }
543:
544: /**
545: * Constructor for local service provider.
546: */
547: private ServiceProvider(BeanContextServiceProvider bcss) {
548: _bcss = bcss;
549: _serializable = _bcss instanceof Serializable;
550: }
551:
552: /**
553: * Add a child reference to this service provider.
554: *
555: * @param requestor
556: * @param bcsrl
557: * @throws TooManyListenersException
558: */
559: private synchronized void addChildReference(Object requestor,
560: BeanContextServiceRevokedListener bcsrl)
561: throws TooManyListenersException {
562:
563: ChildServiceReference csr = getRequestors().get(requestor);
564: if (csr == null) {
565: csr = new ChildServiceReference(bcsrl);
566: getRequestors().put(requestor, csr);
567: return;
568: }
569:
570: if (bcsrl != null && !bcsrl.equals(csr.getListener())) {
571: throw new TooManyListenersException();
572: }
573: csr.incrementRefCount();
574: }
575:
576: /**
577: * Remove a child reference from this Service provider.
578: *
579: * @param requestor
580: */
581: private synchronized void removeChildReference(Object requestor) {
582: ChildServiceReference csr = getRequestors().get(requestor);
583: if (csr == null)
584: return;
585: int refCount = csr.decrementRefCount();
586: if (refCount <= 0) {
587: getRequestors().remove(requestor);
588: }
589: }
590:
591: /**
592: * Notify all the active requestors for this service that the service
593: * has been revoked (per spec).
594: */
595: private synchronized void revoke(
596: BeanContextServiceRevokedEvent bcsre) {
597: for (ChildServiceReference csr : getRequestors().values()) {
598: csr.getListener().serviceRevoked(bcsre);
599: }
600: _revoked = true;
601: }
602:
603: /**
604: * Get the BeanContextServiceProvider.
605: */
606: private BeanContextServiceProvider getBCServiceProvider() {
607: return _bcss;
608: }
609:
610: /**
611: * True if this is a delegated service.
612: */
613: private boolean isDelegated() {
614: return _bcss == null;
615: }
616:
617: /**
618: * True if this service has been revoked, but still has references.
619: */
620: private boolean isRevoked() {
621: return _revoked;
622: }
623:
624: /**
625: * Can this service provider be serialized?
626: */
627: private boolean isSerializable() {
628: return _serializable && !_revoked && !isDelegated();
629: }
630:
631: /**
632: * True if any requestors are being tracked for this service.
633: */
634: private boolean hasRequestors() {
635: return !getRequestors().isEmpty();
636: }
637:
638: /**
639: * Get a reference to the transient requestors hashmap.
640: */
641: private synchronized HashMap<Object, ChildServiceReference> getRequestors() {
642: if (_requestors == null) {
643: _requestors = new HashMap<Object, ChildServiceReference>();
644: }
645: return _requestors;
646: }
647: }
648:
649: /**
650: * Keeps track of the number of references to a service for a single requestor.
651: * Associated with the requestor in the ServiceProvider's _requestors Map.
652: */
653: private static final class ChildServiceReference {
654:
655: private int _refCount;
656: private BeanContextServiceRevokedListener _bcsrl;
657:
658: private ChildServiceReference(
659: BeanContextServiceRevokedListener bcsrl) {
660: _bcsrl = bcsrl;
661: _refCount = 1;
662: }
663:
664: private int incrementRefCount() {
665: return ++_refCount;
666: }
667:
668: private int decrementRefCount() {
669: return --_refCount;
670: }
671:
672: private BeanContextServiceRevokedListener getListener() {
673: return _bcsrl;
674: }
675: }
676: }
|