1 /*
2  * Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package java.beans.beancontext;
27 
28 import java.io.IOException;
29 import java.io.ObjectInputStream;
30 import java.io.ObjectOutputStream;
31 import java.io.Serial;
32 import java.io.Serializable;
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.HashMap;
36 import java.util.HashSet;
37 import java.util.Iterator;
38 import java.util.Locale;
39 import java.util.Map;
40 import java.util.TooManyListenersException;
41 
42 /**
43  * <p>
44  * This helper class provides a utility implementation of the
45  * java.beans.beancontext.BeanContextServices interface.
46  * </p>
47  * <p>
48  * Since this class directly implements the BeanContextServices interface,
49  * the class can, and is intended to be used either by subclassing this
50  * implementation, or via delegation of an instance of this class
51  * from another through the BeanContextProxy interface.
52  * </p>
53  *
54  * @author Laurence P. G. Cable
55  * @since 1.2
56  */
57 
58 public class      BeanContextServicesSupport extends BeanContextSupport
59        implements BeanContextServices {
60 
61     /**
62      * Use serialVersionUID from JDK 1.7 for interoperability.
63      */
64     @Serial
65     private static final long serialVersionUID = -8494482757288719206L;
66 
67     /**
68      * <p>
69      * Construct a BeanContextServicesSupport instance
70      * </p>
71      *
72      * @param peer      The peer BeanContext we are supplying an implementation for, if null the this object is its own peer
73      * @param lcle      The current Locale for this BeanContext.
74      * @param dTime     The initial state, true if in design mode, false if runtime.
75      * @param visible   The initial visibility.
76      *
77      */
78 
BeanContextServicesSupport(BeanContextServices peer, Locale lcle, boolean dTime, boolean visible)79     public BeanContextServicesSupport(BeanContextServices peer, Locale lcle, boolean dTime, boolean visible) {
80         super(peer, lcle, dTime, visible);
81     }
82 
83     /**
84      * Create an instance using the specified Locale and design mode.
85      *
86      * @param peer      The peer BeanContext we are supplying an implementation for, if null the this object is its own peer
87      * @param lcle      The current Locale for this BeanContext.
88      * @param dtime     The initial state, true if in design mode, false if runtime.
89      */
90 
BeanContextServicesSupport(BeanContextServices peer, Locale lcle, boolean dtime)91     public BeanContextServicesSupport(BeanContextServices peer, Locale lcle, boolean dtime) {
92         this (peer, lcle, dtime, true);
93     }
94 
95     /**
96      * Create an instance using the specified locale
97      *
98      * @param peer      The peer BeanContext we are supplying an implementation for, if null the this object is its own peer
99      * @param lcle      The current Locale for this BeanContext.
100      */
101 
BeanContextServicesSupport(BeanContextServices peer, Locale lcle)102     public BeanContextServicesSupport(BeanContextServices peer, Locale lcle) {
103         this (peer, lcle, false, true);
104     }
105 
106     /**
107      * Create an instance with a peer
108      *
109      * @param peer      The peer BeanContext we are supplying an implementation for, if null the this object is its own peer
110      */
111 
BeanContextServicesSupport(BeanContextServices peer)112     public BeanContextServicesSupport(BeanContextServices peer) {
113         this (peer, null, false, true);
114     }
115 
116     /**
117      * Create an instance that is not a delegate of another object
118      */
119 
BeanContextServicesSupport()120     public BeanContextServicesSupport() {
121         this (null, null, false, true);
122     }
123 
124     /**
125      * called by BeanContextSupport superclass during construction and
126      * deserialization to initialize subclass transient state.
127      *
128      * subclasses may envelope this method, but should not override it or
129      * call it directly.
130      */
131 
initialize()132     public void initialize() {
133         super.initialize();
134         services     = new HashMap<>(serializable + 1);
135         bcsListeners = new ArrayList<>(1);
136     }
137 
138     /**
139      * Gets the {@code BeanContextServices} associated with this
140      * {@code BeanContextServicesSupport}.
141      *
142      * @return the instance of {@code BeanContext}
143      * this object is providing the implementation for.
144      */
getBeanContextServicesPeer()145     public BeanContextServices getBeanContextServicesPeer() {
146         return (BeanContextServices)getBeanContextChildPeer();
147     }
148 
149     /************************************************************************/
150 
151     /*
152      * protected nested class containing per child information, an instance
153      * of which is associated with each child in the "children" hashtable.
154      * subclasses can extend this class to include their own per-child state.
155      *
156      * Note that this 'value' is serialized with the corresponding child 'key'
157      * when the BeanContextSupport is serialized.
158      */
159 
160     protected class BCSSChild extends BeanContextSupport.BCSChild  {
161 
162         /**
163          * Use serialVersionUID from JDK 1.7 for interoperability.
164          */
165         @Serial
166         private static final long serialVersionUID = -3263851306889194873L;
167 
168         /*
169          * private nested class to map serviceClass to Provider and requestors
170          * listeners.
171          */
172 
173         class BCSSCServiceClassRef {
174 
175             // create an instance of a service ref
176 
BCSSCServiceClassRef(Class<?> sc, BeanContextServiceProvider bcsp, boolean delegated)177             BCSSCServiceClassRef(Class<?> sc, BeanContextServiceProvider bcsp, boolean delegated) {
178                 super();
179 
180                 serviceClass     = sc;
181 
182                 if (delegated)
183                     delegateProvider = bcsp;
184                 else
185                     serviceProvider  = bcsp;
186             }
187 
188             // add a requestor and assoc listener
189 
addRequestor(Object requestor, BeanContextServiceRevokedListener bcsrl)190             void addRequestor(Object requestor, BeanContextServiceRevokedListener bcsrl) throws TooManyListenersException {
191                 BeanContextServiceRevokedListener cbcsrl = requestors.get(requestor);
192 
193                 if (cbcsrl != null && !cbcsrl.equals(bcsrl))
194                     throw new TooManyListenersException();
195 
196                 requestors.put(requestor, bcsrl);
197             }
198 
199             // remove a requestor
200 
removeRequestor(Object requestor)201             void removeRequestor(Object requestor) {
202                 requestors.remove(requestor);
203             }
204 
205             // check a requestors listener
206 
verifyRequestor(Object requestor, BeanContextServiceRevokedListener bcsrl)207             void verifyRequestor(Object requestor, BeanContextServiceRevokedListener bcsrl) throws TooManyListenersException {
208                 BeanContextServiceRevokedListener cbcsrl = requestors.get(requestor);
209 
210                 if (cbcsrl != null && !cbcsrl.equals(bcsrl))
211                     throw new TooManyListenersException();
212             }
213 
verifyAndMaybeSetProvider(BeanContextServiceProvider bcsp, boolean isDelegated)214             void verifyAndMaybeSetProvider(BeanContextServiceProvider bcsp, boolean isDelegated) {
215                 BeanContextServiceProvider current;
216 
217                 if (isDelegated) { // the provider is delegated
218                     current = delegateProvider;
219 
220                     if (current == null || bcsp == null) {
221                         delegateProvider = bcsp;
222                         return;
223                     }
224                 } else { // the provider is registered with this BCS
225                     current = serviceProvider;
226 
227                     if (current == null || bcsp == null) {
228                         serviceProvider = bcsp;
229                         return;
230                     }
231                 }
232 
233                 if (!current.equals(bcsp))
234                     throw new UnsupportedOperationException("existing service reference obtained from different BeanContextServiceProvider not supported");
235 
236             }
237 
238             @SuppressWarnings("unchecked") // Cast from clone
cloneOfEntries()239             Iterator<Map.Entry<Object, BeanContextServiceRevokedListener>> cloneOfEntries() {
240                 return ((HashMap<Object, BeanContextServiceRevokedListener>)requestors.clone()).entrySet().iterator();
241             }
242 
entries()243             Iterator<Map.Entry<Object, BeanContextServiceRevokedListener>> entries() {
244                 return requestors.entrySet().iterator();
245             }
246 
isEmpty()247             boolean isEmpty() { return requestors.isEmpty(); }
248 
getServiceClass()249             Class<?> getServiceClass() { return serviceClass; }
250 
getServiceProvider()251             BeanContextServiceProvider getServiceProvider() {
252                 return serviceProvider;
253             }
254 
getDelegateProvider()255             BeanContextServiceProvider getDelegateProvider() {
256                 return delegateProvider;
257             }
258 
isDelegated()259             boolean isDelegated() { return delegateProvider != null; }
260 
addRef(boolean delegated)261             void addRef(boolean delegated) {
262                 if (delegated) {
263                     delegateRefs++;
264                 } else {
265                     serviceRefs++;
266                 }
267             }
268 
269 
releaseRef(boolean delegated)270             void releaseRef(boolean delegated) {
271                 if (delegated) {
272                     if (--delegateRefs == 0) {
273                         delegateProvider = null;
274                     }
275                 } else {
276                     if (--serviceRefs  <= 0) {
277                         serviceProvider = null;
278                     }
279                 }
280             }
281 
getRefs()282             int getRefs() { return serviceRefs + delegateRefs; }
283 
getDelegateRefs()284             int getDelegateRefs() { return delegateRefs; }
285 
getServiceRefs()286             int getServiceRefs() { return serviceRefs; }
287 
288             /*
289              * fields
290              */
291 
292             Class<?>                            serviceClass;
293 
294             BeanContextServiceProvider          serviceProvider;
295             int                                 serviceRefs;
296 
297             BeanContextServiceProvider          delegateProvider; // proxy
298             int                                 delegateRefs;
299 
300             HashMap<Object, BeanContextServiceRevokedListener> requestors = new HashMap<>(1);
301         }
302 
303         /*
304          * per service reference info ...
305          */
306 
307         class BCSSCServiceRef {
BCSSCServiceRef(BCSSCServiceClassRef scref, boolean isDelegated)308             BCSSCServiceRef(BCSSCServiceClassRef scref, boolean isDelegated) {
309                 serviceClassRef = scref;
310                 delegated       = isDelegated;
311             }
312 
addRef()313             void addRef()  { refCnt++;        }
release()314             int  release() { return --refCnt; }
315 
getServiceClassRef()316             BCSSCServiceClassRef getServiceClassRef() { return serviceClassRef; }
317 
isDelegated()318             boolean isDelegated() { return delegated; }
319 
320             /*
321              * fields
322              */
323 
324             BCSSCServiceClassRef serviceClassRef;
325             int                  refCnt    = 1;
326             boolean              delegated = false;
327         }
328 
BCSSChild(Object bcc, Object peer)329         BCSSChild(Object bcc, Object peer) { super(bcc, peer); }
330 
331         // note usage of service per requestor, per service
332 
usingService(Object requestor, Object service, Class<?> serviceClass, BeanContextServiceProvider bcsp, boolean isDelegated, BeanContextServiceRevokedListener bcsrl)333         synchronized void usingService(Object requestor, Object service, Class<?> serviceClass, BeanContextServiceProvider bcsp, boolean isDelegated, BeanContextServiceRevokedListener bcsrl)  throws TooManyListenersException, UnsupportedOperationException {
334 
335             // first, process mapping from serviceClass to requestor(s)
336 
337             BCSSCServiceClassRef serviceClassRef = null;
338 
339             if (serviceClasses == null)
340                 serviceClasses = new HashMap<>(1);
341             else
342                 serviceClassRef = serviceClasses.get(serviceClass);
343 
344             if (serviceClassRef == null) { // new service being used ...
345                 serviceClassRef = new BCSSCServiceClassRef(serviceClass, bcsp, isDelegated);
346                 serviceClasses.put(serviceClass, serviceClassRef);
347 
348             } else { // existing service ...
349                 serviceClassRef.verifyAndMaybeSetProvider(bcsp, isDelegated); // throws
350                 serviceClassRef.verifyRequestor(requestor, bcsrl); // throws
351             }
352 
353             serviceClassRef.addRequestor(requestor, bcsrl);
354             serviceClassRef.addRef(isDelegated);
355 
356             // now handle mapping from requestor to service(s)
357 
358             BCSSCServiceRef serviceRef = null;
359             Map<Object , BCSSCServiceRef> services   = null;
360 
361             if (serviceRequestors == null) {
362                 serviceRequestors = new HashMap<>(1);
363             } else {
364                 services = serviceRequestors.get(requestor);
365             }
366 
367             if (services == null) {
368                 services = new HashMap<>(1);
369 
370                 serviceRequestors.put(requestor, services);
371             } else
372                 serviceRef = services.get(service);
373 
374             if (serviceRef == null) {
375                 serviceRef = new BCSSCServiceRef(serviceClassRef, isDelegated);
376 
377                 services.put(service, serviceRef);
378             } else {
379                 serviceRef.addRef();
380             }
381         }
382 
383         // release a service reference
384 
releaseService(Object requestor, Object service)385         synchronized void releaseService(Object requestor, Object service) {
386             if (serviceRequestors == null) return;
387 
388             Map<Object, BCSSCServiceRef> services = serviceRequestors.get(requestor);
389 
390             if (services == null) return; // oops its not there anymore!
391 
392             BCSSCServiceRef serviceRef = services.get(service);
393 
394             if (serviceRef == null) return; // oops its not there anymore!
395 
396             BCSSCServiceClassRef serviceClassRef = serviceRef.getServiceClassRef();
397             boolean                    isDelegated = serviceRef.isDelegated();
398             BeanContextServiceProvider bcsp        = isDelegated ? serviceClassRef.getDelegateProvider() : serviceClassRef.getServiceProvider();
399 
400             bcsp.releaseService(BeanContextServicesSupport.this.getBeanContextServicesPeer(), requestor, service);
401 
402             serviceClassRef.releaseRef(isDelegated);
403             serviceClassRef.removeRequestor(requestor);
404 
405             if (serviceRef.release() == 0) {
406 
407                 services.remove(service);
408 
409                 if (services.isEmpty()) {
410                     serviceRequestors.remove(requestor);
411                     serviceClassRef.removeRequestor(requestor);
412                 }
413 
414                 if (serviceRequestors.isEmpty()) {
415                     serviceRequestors = null;
416                 }
417 
418                 if (serviceClassRef.isEmpty()) {
419                     serviceClasses.remove(serviceClassRef.getServiceClass());
420                 }
421 
422                 if (serviceClasses.isEmpty())
423                     serviceClasses = null;
424             }
425         }
426 
427         // revoke a service
428 
revokeService(Class<?> serviceClass, boolean isDelegated, boolean revokeNow)429         synchronized void revokeService(Class<?> serviceClass, boolean isDelegated, boolean revokeNow) {
430             if (serviceClasses == null) return;
431 
432             BCSSCServiceClassRef serviceClassRef = serviceClasses.get(serviceClass);
433 
434             if (serviceClassRef == null) return;
435 
436             Iterator<Map.Entry<Object, BeanContextServiceRevokedListener>> i = serviceClassRef.cloneOfEntries();
437 
438             BeanContextServiceRevokedEvent bcsre       = new BeanContextServiceRevokedEvent(BeanContextServicesSupport.this.getBeanContextServicesPeer(), serviceClass, revokeNow);
439             boolean                        noMoreRefs  = false;
440 
441             while (i.hasNext() && serviceRequestors != null) {
442                 Map.Entry<Object,BeanContextServiceRevokedListener> entry    = i.next();
443                 BeanContextServiceRevokedListener listener = entry.getValue();
444 
445                 if (revokeNow) {
446                     Object  requestor = entry.getKey();
447                     Map<Object, BCSSCServiceRef> services  = serviceRequestors.get(requestor);
448 
449                     if (services != null) {
450                         Iterator<Map.Entry<Object, BCSSCServiceRef>> i1 = services.entrySet().iterator();
451 
452                         while (i1.hasNext()) {
453                             Map.Entry<Object, BCSSCServiceRef> tmp        = i1.next();
454 
455                             BCSSCServiceRef serviceRef = tmp.getValue();
456                             if (serviceRef.getServiceClassRef().equals(serviceClassRef) && isDelegated == serviceRef.isDelegated()) {
457                                 i1.remove();
458                             }
459                         }
460 
461                         if (noMoreRefs = services.isEmpty()) {
462                             serviceRequestors.remove(requestor);
463                         }
464                     }
465 
466                     if (noMoreRefs) serviceClassRef.removeRequestor(requestor);
467                 }
468 
469                 listener.serviceRevoked(bcsre);
470             }
471 
472             if (revokeNow && serviceClasses != null) {
473                 if (serviceClassRef.isEmpty())
474                     serviceClasses.remove(serviceClass);
475 
476                 if (serviceClasses.isEmpty())
477                     serviceClasses = null;
478             }
479 
480             if (serviceRequestors != null && serviceRequestors.isEmpty())
481                 serviceRequestors = null;
482         }
483 
484         // release all references for this child since it has been unnested.
485 
cleanupReferences()486         void cleanupReferences() {
487 
488             if (serviceRequestors == null) return;
489 
490             Iterator<Map.Entry<Object, Map<Object, BCSSCServiceRef>>> requestors = serviceRequestors.entrySet().iterator();
491 
492             while(requestors.hasNext()) {
493                 Map.Entry<Object, Map<Object, BCSSCServiceRef>> tmp = requestors.next();
494                 Object               requestor = tmp.getKey();
495                 Iterator<Map.Entry<Object, BCSSCServiceRef>> services  = tmp.getValue().entrySet().iterator();
496 
497                 requestors.remove();
498 
499                 while (services.hasNext()) {
500                     Map.Entry<Object, BCSSCServiceRef> entry   = services.next();
501                     Object          service = entry.getKey();
502                     BCSSCServiceRef sref    = entry.getValue();
503 
504                     BCSSCServiceClassRef       scref = sref.getServiceClassRef();
505 
506                     BeanContextServiceProvider bcsp  = sref.isDelegated() ? scref.getDelegateProvider() : scref.getServiceProvider();
507 
508                     scref.removeRequestor(requestor);
509                     services.remove();
510 
511                     while (sref.release() >= 0) {
512                         bcsp.releaseService(BeanContextServicesSupport.this.getBeanContextServicesPeer(), requestor, service);
513                     }
514                 }
515             }
516 
517             serviceRequestors = null;
518             serviceClasses    = null;
519         }
520 
revokeAllDelegatedServicesNow()521         void revokeAllDelegatedServicesNow() {
522             if (serviceClasses == null) return;
523 
524             for (BCSSCServiceClassRef serviceClassRef : new HashSet<>(serviceClasses.values())) {
525                 if (!serviceClassRef.isDelegated()) continue;
526 
527                 Iterator<Map.Entry<Object, BeanContextServiceRevokedListener>> i = serviceClassRef.cloneOfEntries();
528                 BeanContextServiceRevokedEvent bcsre       = new BeanContextServiceRevokedEvent(BeanContextServicesSupport.this.getBeanContextServicesPeer(), serviceClassRef.getServiceClass(), true);
529                 boolean                        noMoreRefs  = false;
530 
531                 while (i.hasNext()) {
532                     Map.Entry<Object, BeanContextServiceRevokedListener> entry     = i.next();
533                     BeanContextServiceRevokedListener listener  = entry.getValue();
534 
535                     Object                            requestor = entry.getKey();
536                     Map<Object, BCSSCServiceRef>      services  = serviceRequestors.get(requestor);
537 
538                     if (services != null) {
539                         Iterator<Map.Entry<Object, BCSSCServiceRef>> i1 = services.entrySet().iterator();
540 
541                         while (i1.hasNext()) {
542                             Map.Entry<Object, BCSSCServiceRef>   tmp        = i1.next();
543 
544                             BCSSCServiceRef serviceRef = tmp.getValue();
545                             if (serviceRef.getServiceClassRef().equals(serviceClassRef) && serviceRef.isDelegated()) {
546                                 i1.remove();
547                             }
548                         }
549 
550                         if (noMoreRefs = services.isEmpty()) {
551                             serviceRequestors.remove(requestor);
552                         }
553                     }
554 
555                     if (noMoreRefs) serviceClassRef.removeRequestor(requestor);
556 
557                     listener.serviceRevoked(bcsre);
558 
559                     if (serviceClassRef.isEmpty())
560                         serviceClasses.remove(serviceClassRef.getServiceClass());
561                 }
562             }
563 
564             if (serviceClasses.isEmpty()) serviceClasses = null;
565 
566             if (serviceRequestors != null && serviceRequestors.isEmpty())
567                 serviceRequestors = null;
568         }
569 
570         /*
571          * fields
572          */
573 
574         private transient HashMap<Class<?>, BCSSCServiceClassRef> serviceClasses;
575         private transient HashMap<Object, Map<Object, BeanContextServicesSupport.BCSSChild.BCSSCServiceRef>> serviceRequestors;
576     }
577 
578     /**
579      * <p>
580      * Subclasses can override this method to insert their own subclass
581      * of Child without having to override add() or the other Collection
582      * methods that add children to the set.
583      * </p>
584      *
585      * @param targetChild the child to create the Child on behalf of
586      * @param peer        the peer if the targetChild and peer are related by BeanContextProxy
587      */
588 
createBCSChild(Object targetChild, Object peer)589     protected BCSChild createBCSChild(Object targetChild, Object peer) {
590         return new BCSSChild(targetChild, peer);
591     }
592 
593     /************************************************************************/
594 
595         /**
596          * subclasses may subclass this nested class to add behaviors for
597          * each BeanContextServicesProvider.
598          */
599 
600         protected static class BCSSServiceProvider implements Serializable {
601 
602             /**
603              * Use serialVersionUID from JDK 1.7 for interoperability.
604              */
605             @Serial
606             private static final long serialVersionUID = 861278251667444782L;
607 
BCSSServiceProvider(Class<?> sc, BeanContextServiceProvider bcsp)608             BCSSServiceProvider(Class<?> sc, BeanContextServiceProvider bcsp) {
609                 super();
610 
611                 serviceProvider = bcsp;
612             }
613 
614             /**
615              * Returns the service provider.
616              * @return the service provider
617              */
getServiceProvider()618             protected BeanContextServiceProvider getServiceProvider() {
619                 return serviceProvider;
620             }
621 
622             /**
623              * The service provider.
624              */
625             @SuppressWarnings("serial") // Not statically typed as Serializable
626             protected BeanContextServiceProvider serviceProvider;
627         }
628 
629         /**
630          * subclasses can override this method to create new subclasses of
631          * BCSSServiceProvider without having to override addService() in
632          * order to instantiate.
633          * @param sc the class
634          * @param bcsp the service provider
635          * @return a service provider without overriding addService()
636          */
637 
createBCSSServiceProvider(Class<?> sc, BeanContextServiceProvider bcsp)638         protected BCSSServiceProvider createBCSSServiceProvider(Class<?> sc, BeanContextServiceProvider bcsp) {
639             return new BCSSServiceProvider(sc, bcsp);
640         }
641 
642     /************************************************************************/
643 
644     /**
645      * add a BeanContextServicesListener
646      *
647      * @throws NullPointerException if the argument is null
648      */
649 
addBeanContextServicesListener(BeanContextServicesListener bcsl)650     public void addBeanContextServicesListener(BeanContextServicesListener bcsl) {
651         if (bcsl == null) throw new NullPointerException("bcsl");
652 
653         synchronized(bcsListeners) {
654             if (bcsListeners.contains(bcsl))
655                 return;
656             else
657                 bcsListeners.add(bcsl);
658         }
659     }
660 
661     /**
662      * remove a BeanContextServicesListener
663      */
664 
removeBeanContextServicesListener(BeanContextServicesListener bcsl)665     public void removeBeanContextServicesListener(BeanContextServicesListener bcsl) {
666         if (bcsl == null) throw new NullPointerException("bcsl");
667 
668         synchronized(bcsListeners) {
669             if (!bcsListeners.contains(bcsl))
670                 return;
671             else
672                 bcsListeners.remove(bcsl);
673         }
674     }
675 
676     /**
677      * add a service
678      * @param serviceClass the service class
679      * @param bcsp the service provider
680      */
681 
addService(Class<?> serviceClass, BeanContextServiceProvider bcsp)682     public boolean addService(Class<?> serviceClass, BeanContextServiceProvider bcsp) {
683         return addService(serviceClass, bcsp, true);
684     }
685 
686     /**
687      * add a service
688      * @param serviceClass the service class
689      * @param bcsp the service provider
690      * @param fireEvent whether or not an event should be fired
691      * @return true if the service was successfully added
692      */
693 
addService(Class<?> serviceClass, BeanContextServiceProvider bcsp, boolean fireEvent)694     protected boolean addService(Class<?> serviceClass, BeanContextServiceProvider bcsp, boolean fireEvent) {
695 
696         if (serviceClass == null) throw new NullPointerException("serviceClass");
697         if (bcsp         == null) throw new NullPointerException("bcsp");
698 
699         synchronized(BeanContext.globalHierarchyLock) {
700             if (services.containsKey(serviceClass))
701                 return false;
702             else {
703                 services.put(serviceClass,  createBCSSServiceProvider(serviceClass, bcsp));
704 
705                 if (bcsp instanceof Serializable) serializable++;
706 
707                 if (!fireEvent) return true;
708 
709 
710                 BeanContextServiceAvailableEvent bcssae = new BeanContextServiceAvailableEvent(getBeanContextServicesPeer(), serviceClass);
711 
712                 fireServiceAdded(bcssae);
713 
714                 synchronized(children) {
715                     for (Object c : children.keySet()) {
716                         if (c instanceof BeanContextServices) {
717                             ((BeanContextServicesListener)c).serviceAvailable(bcssae);
718                         }
719                     }
720                 }
721 
722                 return true;
723             }
724         }
725     }
726 
727     /**
728      * remove a service
729      * @param serviceClass the service class
730      * @param bcsp the service provider
731      * @param revokeCurrentServicesNow whether or not to revoke the service
732      */
733 
revokeService(Class<?> serviceClass, BeanContextServiceProvider bcsp, boolean revokeCurrentServicesNow)734     public void revokeService(Class<?> serviceClass, BeanContextServiceProvider bcsp, boolean revokeCurrentServicesNow) {
735 
736         if (serviceClass == null) throw new NullPointerException("serviceClass");
737         if (bcsp         == null) throw new NullPointerException("bcsp");
738 
739         synchronized(BeanContext.globalHierarchyLock) {
740             if (!services.containsKey(serviceClass)) return;
741 
742             BCSSServiceProvider bcsssp = services.get(serviceClass);
743 
744             if (!bcsssp.getServiceProvider().equals(bcsp))
745                 throw new IllegalArgumentException("service provider mismatch");
746 
747             services.remove(serviceClass);
748 
749             if (bcsp instanceof Serializable) serializable--;
750 
751             Iterator<BeanContextSupport.BCSChild> i = bcsChildren(); // get the BCSChild values.
752 
753             while (i.hasNext()) {
754                 ((BCSSChild)i.next()).revokeService(serviceClass, false, revokeCurrentServicesNow);
755             }
756 
757             fireServiceRevoked(serviceClass, revokeCurrentServicesNow);
758         }
759     }
760 
761     /**
762      * has a service, which may be delegated
763      */
764 
hasService(Class<?> serviceClass)765     public synchronized boolean hasService(Class<?> serviceClass) {
766         if (serviceClass == null) throw new NullPointerException("serviceClass");
767 
768         synchronized(BeanContext.globalHierarchyLock) {
769             if (services.containsKey(serviceClass)) return true;
770 
771             BeanContextServices bcs = null;
772 
773             try {
774                 bcs = (BeanContextServices)getBeanContext();
775             } catch (ClassCastException cce) {
776                 return false;
777             }
778 
779             return bcs == null ? false : bcs.hasService(serviceClass);
780         }
781     }
782 
783     /************************************************************************/
784 
785     /*
786      * a nested subclass used to represent a proxy for serviceClasses delegated
787      * to an enclosing BeanContext.
788      */
789 
790     protected class BCSSProxyServiceProvider implements BeanContextServiceProvider, BeanContextServiceRevokedListener {
791 
BCSSProxyServiceProvider(BeanContextServices bcs)792         BCSSProxyServiceProvider(BeanContextServices bcs) {
793             super();
794 
795             nestingCtxt = bcs;
796         }
797 
getService(BeanContextServices bcs, Object requestor, Class<?> serviceClass, Object serviceSelector)798         public Object getService(BeanContextServices bcs, Object requestor, Class<?> serviceClass, Object serviceSelector) {
799             Object service = null;
800 
801             try {
802                 service = nestingCtxt.getService(bcs, requestor, serviceClass, serviceSelector, this);
803             } catch (TooManyListenersException tmle) {
804                 return null;
805             }
806 
807             return service;
808         }
809 
releaseService(BeanContextServices bcs, Object requestor, Object service)810         public void releaseService(BeanContextServices bcs, Object requestor, Object service) {
811             nestingCtxt.releaseService(bcs, requestor, service);
812         }
813 
getCurrentServiceSelectors(BeanContextServices bcs, Class<?> serviceClass)814         public Iterator<?> getCurrentServiceSelectors(BeanContextServices bcs, Class<?> serviceClass) {
815             return nestingCtxt.getCurrentServiceSelectors(serviceClass);
816         }
817 
serviceRevoked(BeanContextServiceRevokedEvent bcsre)818         public void serviceRevoked(BeanContextServiceRevokedEvent bcsre) {
819             Iterator<BeanContextSupport.BCSChild> i = bcsChildren(); // get the BCSChild values.
820 
821             while (i.hasNext()) {
822                 ((BCSSChild)i.next()).revokeService(bcsre.getServiceClass(), true, bcsre.isCurrentServiceInvalidNow());
823             }
824         }
825 
826         /*
827          * fields
828          */
829 
830         private BeanContextServices nestingCtxt;
831     }
832 
833     /************************************************************************/
834 
835     /**
836      * obtain a service which may be delegated
837      */
838 
getService(BeanContextChild child, Object requestor, Class<?> serviceClass, Object serviceSelector, BeanContextServiceRevokedListener bcsrl)839      public Object getService(BeanContextChild child, Object requestor, Class<?> serviceClass, Object serviceSelector, BeanContextServiceRevokedListener bcsrl) throws TooManyListenersException {
840         if (child        == null) throw new NullPointerException("child");
841         if (serviceClass == null) throw new NullPointerException("serviceClass");
842         if (requestor    == null) throw new NullPointerException("requestor");
843         if (bcsrl        == null) throw new NullPointerException("bcsrl");
844 
845         Object              service = null;
846         BCSSChild           bcsc;
847         BeanContextServices bcssp   = getBeanContextServicesPeer();
848 
849         synchronized(BeanContext.globalHierarchyLock) {
850             synchronized(children) { bcsc = (BCSSChild)children.get(child); }
851 
852             if (bcsc == null) throw new IllegalArgumentException("not a child of this context"); // not a child ...
853 
854             BCSSServiceProvider bcsssp = services.get(serviceClass);
855 
856             if (bcsssp != null) {
857                 BeanContextServiceProvider bcsp = bcsssp.getServiceProvider();
858                 service = bcsp.getService(bcssp, requestor, serviceClass, serviceSelector);
859                 if (service != null) { // do bookkeeping ...
860                     try {
861                         bcsc.usingService(requestor, service, serviceClass, bcsp, false, bcsrl);
862                     } catch (TooManyListenersException tmle) {
863                         bcsp.releaseService(bcssp, requestor, service);
864                         throw tmle;
865                     } catch (UnsupportedOperationException uope) {
866                         bcsp.releaseService(bcssp, requestor, service);
867                         throw uope; // unchecked rt exception
868                     }
869 
870                     return service;
871                 }
872             }
873 
874 
875             if (proxy != null) {
876 
877                 // try to delegate ...
878 
879                 service = proxy.getService(bcssp, requestor, serviceClass, serviceSelector);
880 
881                 if (service != null) { // do bookkeeping ...
882                     try {
883                         bcsc.usingService(requestor, service, serviceClass, proxy, true, bcsrl);
884                     } catch (TooManyListenersException tmle) {
885                         proxy.releaseService(bcssp, requestor, service);
886                         throw tmle;
887                     } catch (UnsupportedOperationException uope) {
888                         proxy.releaseService(bcssp, requestor, service);
889                         throw uope; // unchecked rt exception
890                     }
891 
892                     return service;
893                 }
894             }
895         }
896 
897         return null;
898     }
899 
900     /**
901      * release a service
902      */
903 
releaseService(BeanContextChild child, Object requestor, Object service)904     public void releaseService(BeanContextChild child, Object requestor, Object service) {
905         if (child     == null) throw new NullPointerException("child");
906         if (requestor == null) throw new NullPointerException("requestor");
907         if (service   == null) throw new NullPointerException("service");
908 
909         BCSSChild bcsc;
910 
911         synchronized(BeanContext.globalHierarchyLock) {
912                 synchronized(children) { bcsc = (BCSSChild)children.get(child); }
913 
914                 if (bcsc != null)
915                     bcsc.releaseService(requestor, service);
916                 else
917                    throw new IllegalArgumentException("child actual is not a child of this BeanContext");
918         }
919     }
920 
921     /**
922      * @return an iterator for all the currently registered service classes.
923      */
924 
getCurrentServiceClasses()925     public Iterator<Object> getCurrentServiceClasses() {
926         return new BCSIterator(services.keySet().iterator());
927     }
928 
929     /**
930      * @return an iterator for all the currently available service selectors
931      * (if any) available for the specified service.
932      */
933 
getCurrentServiceSelectors(Class<?> serviceClass)934     public Iterator<?> getCurrentServiceSelectors(Class<?> serviceClass) {
935 
936         BCSSServiceProvider bcsssp = services.get(serviceClass);
937 
938         return bcsssp != null ? new BCSIterator(bcsssp.getServiceProvider().getCurrentServiceSelectors(getBeanContextServicesPeer(), serviceClass)) : null;
939     }
940 
941     /**
942      * BeanContextServicesListener callback, propagates event to all
943      * currently registered listeners and BeanContextServices children,
944      * if this BeanContextService does not already implement this service
945      * itself.
946      *
947      * subclasses may override or envelope this method to implement their
948      * own propagation semantics.
949      */
950 
serviceAvailable(BeanContextServiceAvailableEvent bcssae)951      public void serviceAvailable(BeanContextServiceAvailableEvent bcssae) {
952         synchronized(BeanContext.globalHierarchyLock) {
953             if (services.containsKey(bcssae.getServiceClass())) return;
954 
955             fireServiceAdded(bcssae);
956 
957             Iterator<Object> i;
958 
959             synchronized(children) {
960                 i = children.keySet().iterator();
961             }
962 
963             while (i.hasNext()) {
964                 Object c = i.next();
965 
966                 if (c instanceof BeanContextServices) {
967                     ((BeanContextServicesListener)c).serviceAvailable(bcssae);
968                 }
969             }
970         }
971      }
972 
973     /**
974      * BeanContextServicesListener callback, propagates event to all
975      * currently registered listeners and BeanContextServices children,
976      * if this BeanContextService does not already implement this service
977      * itself.
978      *
979      * subclasses may override or envelope this method to implement their
980      * own propagation semantics.
981      */
982 
serviceRevoked(BeanContextServiceRevokedEvent bcssre)983     public void serviceRevoked(BeanContextServiceRevokedEvent bcssre) {
984         synchronized(BeanContext.globalHierarchyLock) {
985             if (services.containsKey(bcssre.getServiceClass())) return;
986 
987             fireServiceRevoked(bcssre);
988 
989             Iterator<Object> i;
990 
991             synchronized(children) {
992                 i = children.keySet().iterator();
993             }
994 
995             while (i.hasNext()) {
996                 Object c = i.next();
997 
998                 if (c instanceof BeanContextServices) {
999                     ((BeanContextServicesListener)c).serviceRevoked(bcssre);
1000                 }
1001             }
1002         }
1003     }
1004 
1005     /**
1006      * Gets the {@code BeanContextServicesListener} (if any) of the specified
1007      * child.
1008      *
1009      * @param child the specified child
1010      * @return the BeanContextServicesListener (if any) of the specified child
1011      */
getChildBeanContextServicesListener(Object child)1012     protected static final BeanContextServicesListener getChildBeanContextServicesListener(Object child) {
1013         try {
1014             return (BeanContextServicesListener)child;
1015         } catch (ClassCastException cce) {
1016             return null;
1017         }
1018     }
1019 
1020     /**
1021      * called from superclass child removal operations after a child
1022      * has been successfully removed. called with child synchronized.
1023      *
1024      * This subclass uses this hook to immediately revoke any services
1025      * being used by this child if it is a BeanContextChild.
1026      *
1027      * subclasses may envelope this method in order to implement their
1028      * own child removal side-effects.
1029      */
1030 
childJustRemovedHook(Object child, BCSChild bcsc)1031     protected void childJustRemovedHook(Object child, BCSChild bcsc) {
1032         BCSSChild bcssc = (BCSSChild)bcsc;
1033 
1034         bcssc.cleanupReferences();
1035     }
1036 
1037     /**
1038      * called from setBeanContext to notify a BeanContextChild
1039      * to release resources obtained from the nesting BeanContext.
1040      *
1041      * This method revokes any services obtained from its parent.
1042      *
1043      * subclasses may envelope this method to implement their own semantics.
1044      */
1045 
releaseBeanContextResources()1046     protected synchronized void releaseBeanContextResources() {
1047         Object[] bcssc;
1048 
1049         super.releaseBeanContextResources();
1050 
1051         synchronized(children) {
1052             if (children.isEmpty()) return;
1053 
1054             bcssc = children.values().toArray();
1055         }
1056 
1057 
1058         for (int i = 0; i < bcssc.length; i++) {
1059             ((BCSSChild)bcssc[i]).revokeAllDelegatedServicesNow();
1060         }
1061 
1062         proxy = null;
1063     }
1064 
1065     /**
1066      * called from setBeanContext to notify a BeanContextChild
1067      * to allocate resources obtained from the nesting BeanContext.
1068      *
1069      * subclasses may envelope this method to implement their own semantics.
1070      */
1071 
initializeBeanContextResources()1072     protected synchronized void initializeBeanContextResources() {
1073         super.initializeBeanContextResources();
1074 
1075         BeanContext nbc = getBeanContext();
1076 
1077         if (nbc == null) return;
1078 
1079         try {
1080             BeanContextServices bcs = (BeanContextServices)nbc;
1081 
1082             proxy = new BCSSProxyServiceProvider(bcs);
1083         } catch (ClassCastException cce) {
1084             // do nothing ...
1085         }
1086     }
1087 
1088     /**
1089      * Fires a {@code BeanContextServiceEvent} notifying of a new service.
1090      * @param serviceClass the service class
1091      */
fireServiceAdded(Class<?> serviceClass)1092     protected final void fireServiceAdded(Class<?> serviceClass) {
1093         BeanContextServiceAvailableEvent bcssae = new BeanContextServiceAvailableEvent(getBeanContextServicesPeer(), serviceClass);
1094 
1095         fireServiceAdded(bcssae);
1096     }
1097 
1098     /**
1099      * Fires a {@code BeanContextServiceAvailableEvent} indicating that a new
1100      * service has become available.
1101      *
1102      * @param bcssae the {@code BeanContextServiceAvailableEvent}
1103      */
fireServiceAdded(BeanContextServiceAvailableEvent bcssae)1104     protected final void fireServiceAdded(BeanContextServiceAvailableEvent bcssae) {
1105         Object[]                         copy;
1106 
1107         synchronized (bcsListeners) { copy = bcsListeners.toArray(); }
1108 
1109         for (int i = 0; i < copy.length; i++) {
1110             ((BeanContextServicesListener)copy[i]).serviceAvailable(bcssae);
1111         }
1112     }
1113 
1114     /**
1115      * Fires a {@code BeanContextServiceEvent} notifying of a service being revoked.
1116      *
1117      * @param bcsre the {@code BeanContextServiceRevokedEvent}
1118      */
fireServiceRevoked(BeanContextServiceRevokedEvent bcsre)1119     protected final void fireServiceRevoked(BeanContextServiceRevokedEvent bcsre) {
1120         Object[]                         copy;
1121 
1122         synchronized (bcsListeners) { copy = bcsListeners.toArray(); }
1123 
1124         for (int i = 0; i < copy.length; i++) {
1125             ((BeanContextServiceRevokedListener)copy[i]).serviceRevoked(bcsre);
1126         }
1127     }
1128 
1129     /**
1130      * Fires a {@code BeanContextServiceRevokedEvent}
1131      * indicating that a particular service is
1132      * no longer available.
1133      * @param serviceClass the service class
1134      * @param revokeNow whether or not the event should be revoked now
1135      */
fireServiceRevoked(Class<?> serviceClass, boolean revokeNow)1136     protected final void fireServiceRevoked(Class<?> serviceClass, boolean revokeNow) {
1137         Object[]                       copy;
1138         BeanContextServiceRevokedEvent bcsre = new BeanContextServiceRevokedEvent(getBeanContextServicesPeer(), serviceClass, revokeNow);
1139 
1140         synchronized (bcsListeners) { copy = bcsListeners.toArray(); }
1141 
1142         for (int i = 0; i < copy.length; i++) {
1143             ((BeanContextServicesListener)copy[i]).serviceRevoked(bcsre);
1144         }
1145    }
1146 
1147     /**
1148      * called from BeanContextSupport writeObject before it serializes the
1149      * children ...
1150      *
1151      * This class will serialize any Serializable BeanContextServiceProviders
1152      * herein.
1153      *
1154      * subclasses may envelope this method to insert their own serialization
1155      * processing that has to occur prior to serialization of the children
1156      */
1157 
bcsPreSerializationHook(ObjectOutputStream oos)1158     protected synchronized void bcsPreSerializationHook(ObjectOutputStream oos) throws IOException {
1159 
1160         oos.writeInt(serializable);
1161 
1162         if (serializable <= 0) return;
1163 
1164         int count = 0;
1165 
1166         Iterator<Map.Entry<Object, BCSSServiceProvider>> i = services.entrySet().iterator();
1167 
1168         while (i.hasNext() && count < serializable) {
1169             Map.Entry<Object, BCSSServiceProvider> entry = i.next();
1170             BCSSServiceProvider bcsp  = null;
1171 
1172              try {
1173                 bcsp = entry.getValue();
1174              } catch (ClassCastException cce) {
1175                 continue;
1176              }
1177 
1178              if (bcsp.getServiceProvider() instanceof Serializable) {
1179                 oos.writeObject(entry.getKey());
1180                 oos.writeObject(bcsp);
1181                 count++;
1182              }
1183         }
1184 
1185         if (count != serializable)
1186             throw new IOException("wrote different number of service providers than expected");
1187     }
1188 
1189     /**
1190      * called from BeanContextSupport readObject before it deserializes the
1191      * children ...
1192      *
1193      * This class will deserialize any Serializable BeanContextServiceProviders
1194      * serialized earlier thus making them available to the children when they
1195      * deserialized.
1196      *
1197      * subclasses may envelope this method to insert their own serialization
1198      * processing that has to occur prior to serialization of the children
1199      */
1200 
bcsPreDeserializationHook(ObjectInputStream ois)1201     protected synchronized void bcsPreDeserializationHook(ObjectInputStream ois) throws IOException, ClassNotFoundException {
1202 
1203         serializable = ois.readInt();
1204 
1205         int count = serializable;
1206 
1207         while (count > 0) {
1208             services.put(ois.readObject(), (BCSSServiceProvider)ois.readObject());
1209             count--;
1210         }
1211     }
1212 
1213     /**
1214      * Serialize the instance.
1215      *
1216      * @param  oos the {@code ObjectOutputStream} to write
1217      * @throws IOException if an I/O error occurs
1218      */
1219     @Serial
writeObject(ObjectOutputStream oos)1220     private synchronized void writeObject(ObjectOutputStream oos) throws IOException {
1221         oos.defaultWriteObject();
1222 
1223         serialize(oos, (Collection)bcsListeners);
1224     }
1225 
1226     /**
1227      * Deserialize the instance.
1228      *
1229      * @param  ois the {@code ObjectInputStream} to read
1230      * @throws ClassNotFoundException if the class of a serialized object could
1231      *         not be found
1232      * @throws IOException if an I/O error occurs
1233      */
1234     @Serial
readObject(ObjectInputStream ois)1235     private synchronized void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
1236 
1237         ois.defaultReadObject();
1238 
1239         deserialize(ois, (Collection)bcsListeners);
1240     }
1241 
1242 
1243     /*
1244      * fields
1245      */
1246 
1247     /**
1248      * all accesses to the {@code protected transient HashMap services}
1249      * field should be synchronized on that object
1250      */
1251     protected transient HashMap<Object, BCSSServiceProvider>  services;
1252 
1253     /**
1254      * The number of instances of a serializable {@code BeanContextServceProvider}.
1255      */
1256     protected transient int                      serializable = 0;
1257 
1258 
1259     /**
1260      * Delegate for the {@code BeanContextServiceProvider}.
1261      */
1262     protected transient BCSSProxyServiceProvider proxy;
1263 
1264 
1265     /**
1266      * List of {@code BeanContextServicesListener} objects.
1267      */
1268     protected transient ArrayList<BeanContextServicesListener> bcsListeners;
1269 }
1270