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