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