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 
  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         services     = new HashMap<>(serializable + 1);
 133         bcsListeners = new ArrayList<>(1);
 134     }
 135 
 136     /**
 137      * Gets the <tt>BeanContextServices</tt> associated with this
 138      * <tt>BeanContextServicesSupport</tt>.
 139      *
 140      * @return the instance of <tt>BeanContext</tt>
 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 <tt>BeanContextServicesListener</tt> (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 <tt>BeanContextServiceEvent</tt> 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 <tt>BeanContextServiceAvailableEvent</tt> indicating that a new
1098      * service has become available.
1099      *
1100      * @param bcssae the <tt>BeanContextServiceAvailableEvent</tt>
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 <tt>BeanContextServiceEvent</tt> notifying of a service being revoked.
1114      *
1115      * @param bcsre the <tt>BeanContextServiceRevokedEvent</tt>
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 <tt>BeanContextServiceRevokedEvent</tt>
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 </code>
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 <tt>BeanContextServceProvider</tt>.
1245      */
1246     protected transient int                      serializable = 0;
1247 
1248 
1249     /**
1250      * Delegate for the <tt>BeanContextServiceProvider</tt>.
1251      */
1252     protected transient BCSSProxyServiceProvider proxy;
1253 
1254 
1255     /**
1256      * List of <tt>BeanContextServicesListener</tt> objects.
1257      */
1258     protected transient ArrayList<BeanContextServicesListener> bcsListeners;
1259 }