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