001    /* BeanContextServicesSupport.java --
002       Copyright (C) 2003, 2005  Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package java.beans.beancontext;
040    
041    import java.io.IOException;
042    import java.io.ObjectInputStream;
043    import java.io.ObjectOutputStream;
044    import java.io.Serializable;
045    import java.util.ArrayList;
046    import java.util.HashMap;
047    import java.util.HashSet;
048    import java.util.Iterator;
049    import java.util.List;
050    import java.util.Locale;
051    import java.util.Set;
052    import java.util.TooManyListenersException;
053    
054    /**
055     * This is a helper class for implementing a bean context which
056     * supplies services.  It is intended to be used either by
057     * subclassing or by calling methods of this implementation
058     * from another.
059     *
060     * @author Michael Koch
061     * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
062     * @since 1.2
063     */
064    public class BeanContextServicesSupport
065      extends BeanContextSupport
066      implements BeanContextServices
067    {
068      private static final long serialVersionUID = -8494482757288719206L;
069    
070      protected class BCSSChild
071        extends BeanContextSupport.BCSChild
072      {
073        private static final long serialVersionUID = -3263851306889194873L;
074    
075        BCSSChild(Object targetChild, Object peer)
076        {
077          super(targetChild, peer);
078        }
079      }
080    
081      protected class BCSSProxyServiceProvider
082        implements BeanContextServiceProvider,
083        BeanContextServiceRevokedListener
084      {
085        private static final long serialVersionUID = 7078212910685744490L;
086    
087        private BeanContextServiceProvider provider;
088    
089        BCSSProxyServiceProvider(BeanContextServiceProvider p)
090        {
091          provider = p;
092        }
093    
094        public Iterator getCurrentServiceSelectors (BeanContextServices bcs,
095                                                    Class serviceClass)
096        {
097          return provider.getCurrentServiceSelectors(bcs, serviceClass);
098        }
099    
100        public Object getService (BeanContextServices bcs,
101                                  Object requestor,
102                                  Class serviceClass,
103                                  Object serviceSelector)
104        {
105          return provider.getService(bcs, requestor, serviceClass,
106                                     serviceSelector);
107        }
108    
109        public void releaseService (BeanContextServices bcs,
110                                    Object requestor,
111                                    Object service)
112        {
113          provider.releaseService(bcs, requestor, service);
114        }
115    
116        public void serviceRevoked (BeanContextServiceRevokedEvent bcsre)
117        {
118          if (provider instanceof BeanContextServiceRevokedListener)
119            ((BeanContextServiceRevokedListener) provider).serviceRevoked(bcsre);
120        }
121      }
122    
123      protected static class BCSSServiceProvider
124        implements Serializable
125      {
126        private static final long serialVersionUID = 861278251667444782L;
127    
128        protected BeanContextServiceProvider serviceProvider;
129    
130        private Class serviceClass;
131    
132        private BCSSServiceProvider(Class serviceClass,
133                                    BeanContextServiceProvider provider)
134        {
135          this.serviceClass = serviceClass;
136          serviceProvider = provider;
137        }
138    
139        protected BeanContextServiceProvider getServiceProvider()
140        {
141          return serviceProvider;
142        }
143    
144        private Class getServiceClass()
145        {
146          return serviceClass;
147        }
148    
149      }
150    
151      /**
152       * Represents a request for a service.  This is
153       * a common superclass used by the classes which maintain
154       * the listener-requestor and service-requestor relationships.
155       *
156       * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
157       */
158      private static abstract class Request
159      {
160        private Object requestor;
161    
162        public Request(Object requestor)
163        {
164          this.requestor = requestor;
165        }
166    
167        public boolean equals(Object obj)
168        {
169          if (obj instanceof Request)
170            {
171              Request req = (Request) obj;
172              return req.getRequestor().equals(requestor);
173            }
174          return false;
175        }
176    
177        public Object getRequestor()
178        {
179          return requestor;
180        }
181    
182      }
183    
184      /**
185       * Represents a relationship between a service requestor
186       * and a revocation listener.
187       *
188       * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
189       */
190      private static class ServiceRequest
191        extends Request
192      {
193    
194        private BeanContextServiceRevokedListener listener;
195    
196        public ServiceRequest(Object requestor,
197                              BeanContextServiceRevokedListener listener)
198        {
199          super(requestor);
200          this.listener = listener;
201        }
202    
203        public boolean equals(Object obj)
204        {
205          if (obj instanceof ServiceRequest)
206            {
207              ServiceRequest sr = (ServiceRequest) obj;
208              return (super.equals(obj) &&
209                      sr.getListener().equals(listener));
210            }
211          return false;
212        }
213    
214        public BeanContextServiceRevokedListener getListener()
215        {
216          return listener;
217        }
218      }
219    
220      /**
221       * Represents a relationship between a service requestor
222       * and a service instance.
223       *
224       * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
225       */
226      private static class ServiceLease
227        extends Request
228      {
229    
230        private Object service;
231    
232        public ServiceLease(Object requestor, Object service)
233        {
234          super(requestor);
235          this.service = service;
236        }
237    
238        public boolean equals(Object obj)
239        {
240          if (obj instanceof ServiceLease)
241            {
242              ServiceLease sl = (ServiceLease) obj;
243              return (super.equals(obj) &&
244                      sl.getService().equals(service));
245            }
246          return false;
247        }
248    
249        public Object getService()
250        {
251          return service;
252        }
253      }
254    
255      /**
256       * A collection of listeners who receive availability
257       * and revocation notifications.
258       */
259      protected transient ArrayList bcsListeners;
260    
261      protected transient BCSSProxyServiceProvider proxy;
262    
263      /**
264       * The number of serializable service providers.
265       */
266      protected transient int serializable;
267    
268      /**
269       * A map of registered services, linking the service
270       * class to its associated {@link BCSSServiceProvider}.
271       */
272      protected transient HashMap services;
273    
274      /**
275       * A map of children to a list of services they
276       * have obtained.
277       */
278      private transient HashMap serviceUsers;
279    
280      /**
281       * A map of services to {@link ServiceRequest}s.
282       */
283      private transient HashMap serviceRequests;
284    
285      /**
286       * A map of {@link ServiceLease}s to providers.
287       */
288      private transient HashMap serviceLeases;
289    
290      /**
291       * Construct a {@link BeanContextServicesSupport} instance.
292       */
293      public BeanContextServicesSupport ()
294      {
295        super();
296      }
297    
298      /**
299       * Construct a {@link BeanContextServicesSupport} instance.
300       *
301       * @param peer the bean context services peer (<code>null</code> permitted).
302       */
303      public BeanContextServicesSupport (BeanContextServices peer)
304      {
305        super(peer);
306      }
307    
308      /**
309       * Construct a {@link BeanContextServicesSupport} instance.
310       *
311       * @param peer the bean context peer (<code>null</code> permitted).
312       * @param locale the locale (<code>null</code> permitted, equivalent to
313       *     the default locale).
314       */
315      public BeanContextServicesSupport(BeanContextServices peer, Locale locale)
316      {
317        super(peer, locale);
318      }
319    
320      /**
321       * Construct a {@link BeanContextServicesSupport} instance.
322       *
323       * @param peer  the bean context peer (<code>null</code> permitted).
324       * @param locale  the locale (<code>null</code> permitted, equivalent to
325       *     the default locale).
326       * @param dtime  a flag indicating whether or not the bean context is in
327       *     design time mode.
328       */
329      public BeanContextServicesSupport(BeanContextServices peer, Locale locale,
330                                        boolean dtime)
331      {
332        super(peer, locale, dtime);
333      }
334    
335      /**
336       * Construct a {@link BeanContextServicesSupport} instance.
337       *
338       * @param peer  the bean context peer (<code>null</code> permitted).
339       * @param locale  the locale (<code>null</code> permitted, equivalent to
340       *     the default locale).
341       * @param dtime  a flag indicating whether or not the bean context is in
342       *     design time mode.
343       * @param visible  initial value of the <code>okToUseGui</code> flag.
344       */
345      public BeanContextServicesSupport(BeanContextServices peer, Locale locale,
346                                        boolean dtime, boolean visible)
347      {
348        super(peer, locale, dtime, visible);
349      }
350    
351      /**
352       * Adds a new listener for service availability and
353       * revocation events.
354       *
355       * @param listener the listener to add.
356       */
357      public void addBeanContextServicesListener
358        (BeanContextServicesListener listener)
359      {
360        synchronized (bcsListeners)
361          {
362            if (! bcsListeners.contains(listener))
363              bcsListeners.add(listener);
364          }
365      }
366    
367      /**
368       * Registers a new service from the specified service provider.
369       * The service is internally associated with the service provider
370       * and a <code>BeanContextServiceAvailableEvent</code> is fired.  If
371       * the service is already registered, then this method instead
372       * returns <code>false</code>.  This is equivalent to calling
373       * <code>addService(serviceClass, bcsp, true)</code>.
374       *
375       * @param serviceClass the class of the service to be registered.
376       * @param bcsp the provider of the given service.
377       * @return true if the service was registered successfully.
378       * @see #addService(Class, BeanContextServiceProvider, boolean)
379       */
380      public boolean addService (Class serviceClass,
381                                 BeanContextServiceProvider bcsp)
382      {
383        return addService(serviceClass, bcsp, true);
384      }
385    
386      /**
387       * Registers a new service from the specified service provider.
388       * The service is internally associated with the service provider
389       * and (if <code>fireEvent</code> is true) a
390       * <code>BeanContextServiceAvailableEvent</code> is fired.  If
391       * the service is already registered, then this method instead
392       * returns <code>false</code>.
393       *
394       * @param serviceClass the class of the service to be registered.
395       * @param bcsp the provider of the given service.
396       * @param fireEvent true if a service availability event should
397       *                  be fired.
398       * @return true if the service was registered successfully.
399       */
400      protected boolean addService (Class serviceClass,
401                                    BeanContextServiceProvider bcsp,
402                                    boolean fireEvent)
403      {
404        synchronized (globalHierarchyLock)
405          {
406            synchronized (services)
407              {
408                if (services.containsKey(serviceClass))
409                  return false;
410                services.put(serviceClass,
411                             createBCSSServiceProvider(serviceClass, bcsp));
412                if (bcsp instanceof Serializable)
413                  ++serializable;
414                if (fireEvent)
415                  fireServiceAdded(serviceClass);
416                return true;
417              }
418          }
419      }
420    
421      /**
422       * Deserializes any service providers which are serializable.  This
423       * method is called by the <code>readObject</code> method of
424       * {@link BeanContextSupport} prior to deserialization of the children.
425       * Subclasses may envelope its behaviour in order to read further
426       * serialized data to the stream.
427       *
428       * @param ois the stream from which data is being deserialized.
429       * @throws IOException if an I/O error occurs.
430       * @throws ClassNotFoundException if the class of a deserialized object
431       *                                can not be found.
432       */
433      protected void bcsPreDeserializationHook (ObjectInputStream ois)
434        throws ClassNotFoundException, IOException
435      {
436        serializable = ois.readInt();
437        for (int a = 0; a < serializable; ++a)
438          {
439            BCSSServiceProvider bcsssp = (BCSSServiceProvider) ois.readObject();
440            addService(bcsssp.getServiceClass(), bcsssp.getServiceProvider());
441          }
442      }
443    
444      /**
445       * Serializes any service providers which are serializable.  This
446       * method is called by the <code>writeObject</code> method of
447       * {@link BeanContextSupport} prior to serialization of the children.
448       * Subclasses may envelope its behaviour in order to add further
449       * serialized data to the stream.
450       *
451       * @param oos the stream to which data is being serialized.
452       * @throws IOException if an I/O error occurs.
453       */
454      protected void bcsPreSerializationHook (ObjectOutputStream oos)
455        throws IOException
456      {
457        oos.writeInt(serializable);
458        synchronized (services)
459          {
460            Iterator i = services.values().iterator();
461            while (i.hasNext())
462              {
463                BCSSServiceProvider bcsssp = (BCSSServiceProvider) i.next();
464                if (bcsssp.getServiceProvider() instanceof Serializable)
465                  oos.writeObject(bcsssp);
466              }
467          }
468      }
469    
470      /**
471       * Revokes any services used by a child that has just been removed.
472       * The superclass ({@link BeanContextSupport}) calls this method
473       * when a child has just been successfully removed.  Subclasses can
474       * extend this method in order to perform additional operations
475       * on child removal.
476       *
477       * @param child the child being removed.
478       * @param bcsc the support object for the child.
479       */
480      protected void childJustRemovedHook (Object child,
481                                           BeanContextSupport.BCSChild bcsc)
482      {
483        if (child instanceof BeanContextChild)
484          {
485            BeanContextChild bcchild = (BeanContextChild) child;
486            Iterator childServices = ((List) serviceUsers.get(bcchild)).iterator();
487            while (childServices.hasNext())
488              releaseService(bcchild, this, childServices.next());
489            serviceUsers.remove(bcchild);
490          }
491      }
492    
493      /**
494       * Overrides the {@link BeanContextSupport#createBCSChild} method
495       * so as to use a {@link BCSSChild} instead.
496       *
497       * @param targetChild the child to create the child for.
498       * @param peer the peer which relates to the child if a proxy is used.
499       * @return a new instance of {@link BCSSChild}.
500       */
501      protected BeanContextSupport.BCSChild createBCSChild (Object targetChild,
502                                                            Object peer)
503      {
504        return new BCSSChild(targetChild, peer);
505      }
506    
507      /**
508       * Provides a hook so that subclasses can replace the
509       * {@link BCSSServiceProvider} class, used to store registered
510       * service providers, with a subclass without replacing the
511       * {@link #addService(Class, BeanContextServiceProvider)} method.
512       *
513       * @param sc the class of service being registered.
514       * @param bcsp the provider of the service.
515       * @return a instance of {@link BCSSServiceProvider} wrapping the provider.
516       */
517      protected BeanContextServicesSupport.BCSSServiceProvider
518      createBCSSServiceProvider (Class sc, BeanContextServiceProvider bcsp)
519      {
520        return new BCSSServiceProvider(sc, bcsp);
521      }
522    
523      /**
524       * Sends a <code>BeanContextServiceAvailableEvent</code> to all
525       * registered listeners.
526       *
527       * @param bcssae the event to send.
528       */
529      protected final void fireServiceAdded (BeanContextServiceAvailableEvent bcssae)
530      {
531        synchronized (bcsListeners)
532          {
533            int size = bcsListeners.size();
534            for (int i = 0; i < size; ++i)
535              {
536                BeanContextServicesListener bcsl
537                  = (BeanContextServicesListener) bcsListeners.get(i);
538                bcsl.serviceAvailable(bcssae);
539              }
540          }
541      }
542    
543      /**
544       * Sends a <code>BeanContextServiceAvailableEvent</code> to all
545       * registered listeners.
546       *
547       * @param serviceClass the service that is now available.
548       * @see #fireServiceAdded(BeanContextServiceAvailableEvent)
549       */
550      protected final void fireServiceAdded (Class serviceClass)
551      {
552        fireServiceAdded(new BeanContextServiceAvailableEvent(this,
553                                                              serviceClass));
554      }
555    
556      /**
557       * Sends a <code>BeanContextServiceRevokedEvent</code> to all
558       * registered listeners.
559       *
560       * @param event the event to send.
561       */
562      protected final void fireServiceRevoked(BeanContextServiceRevokedEvent event)
563      {
564        synchronized (bcsListeners)
565          {
566            int size = bcsListeners.size();
567            for (int i = 0; i < size; ++i)
568              {
569                BeanContextServicesListener bcsl
570                  = (BeanContextServicesListener) bcsListeners.get(i);
571                bcsl.serviceRevoked(event);
572              }
573            List requests = (List) serviceRequests.get(event.getServiceClass());
574            if (requests != null)
575              {
576                Iterator i = requests.iterator();
577                while (i.hasNext())
578                  {
579                    ServiceRequest r = (ServiceRequest) i.next();
580                    r.getListener().serviceRevoked(event);
581                  }
582              }
583          }
584      }
585    
586      /**
587       * Sends a <code>BeanContextServiceRevokedEvent</code> to all
588       * registered listeners.
589       *
590       * @param serviceClass the service that has been revoked.
591       * @see #fireServiceRevoked(BeanContextServiceRevokedEvent)
592       */
593      protected final void fireServiceRevoked (Class serviceClass,
594                                               boolean revokeNow)
595      {
596        fireServiceRevoked(new BeanContextServiceRevokedEvent(this, serviceClass,
597                                                              revokeNow));
598      }
599    
600      /**
601       * Returns the services peer given at construction time,
602       * or <code>null</code> if no peer was given.
603       *
604       * @return the {@link BeanContextServices} peer.
605       */
606      public BeanContextServices getBeanContextServicesPeer ()
607      {
608        return (BeanContextServices) beanContextChildPeer;
609      }
610    
611      /**
612       * Returns <code>child</code> as an instance of
613       * {@link BeanContextServicesListener}, or <code>null</code> if
614       * <code>child</code> does not implement that interface.
615       *
616       * @param child  the child (<code>null</code> permitted).
617       *
618       * @return The child cast to {@link BeanContextServicesListener}.
619       */
620      protected static final BeanContextServicesListener
621          getChildBeanContextServicesListener(Object child)
622      {
623        if (child instanceof BeanContextServicesListener)
624          return (BeanContextServicesListener) child;
625        else
626          return null;
627      }
628    
629      /**
630       * Returns an iterator over the currently available
631       * services.
632       *
633       * @return an iterator over the currently available services.
634       */
635      public Iterator getCurrentServiceClasses ()
636      {
637        synchronized (globalHierarchyLock)
638          {
639            synchronized (services)
640              {
641                return services.keySet().iterator();
642              }
643          }
644      }
645    
646      /**
647       * Returns an iterator over the service selectors of the service
648       * provider for the given service.  The iterator is actually
649       * obtained by calling the
650       * {@link BeanContextServiceProvider#getCurrentServiceSelectors}
651       * of the provider itself.  If the specified service is not available,
652       * <code>null</code> is returned.
653       *
654       * @param serviceClass the service whose provider's selectors should
655       *                     be iterated over.
656       * @return an {@link Iterator} over the service selectors of the
657       *         provider of the given service.
658       */
659      public Iterator getCurrentServiceSelectors (Class serviceClass)
660      {
661        synchronized (globalHierarchyLock)
662          {
663            synchronized (services)
664              {
665                BeanContextServiceProvider bcsp
666                  = ((BCSSServiceProvider)
667                     services.get(serviceClass)).getServiceProvider();
668                if (bcsp == null)
669                  return null;
670                else
671                  return bcsp.getCurrentServiceSelectors(this, serviceClass);
672              }
673          }
674      }
675    
676      /**
677       * Retrieves the specified service.  If a provider for the service
678       * is registered in this context, then the request is passed on to
679       * the provider and the service returned.  Otherwise, the request
680       * is delegated to a parent {@link BeanContextServices}, if possible.
681       * If the service can not be found at all, then <code>null</code>
682       * is returned.
683       *
684       * @param child the child obtaining the reference.
685       * @param requestor the requestor of the service, which may be the
686       *                  child itself.
687       * @param serviceClass the service being requested.
688       * @param serviceSelector an additional service-dependent parameter
689       *                        (may be <code>null</code> if not appropriate).
690       * @param bcsrl a listener used to notify the requestor that the service
691       *              has since been revoked.
692       * @return a reference to the service requested, or <code>null</code>.
693       * @throws TooManyListenersException according to Sun's documentation.
694       */
695      public Object getService (BeanContextChild child, Object requestor,
696                                Class serviceClass, Object serviceSelector,
697                                BeanContextServiceRevokedListener bcsrl)
698        throws TooManyListenersException
699      {
700        synchronized (globalHierarchyLock)
701          {
702            synchronized (services)
703              {
704                Object service;
705                BeanContextServiceProvider provider = ((BCSSServiceProvider)
706                  services.get(serviceClass)).getServiceProvider();
707                if (provider != null)
708                  {
709                    service = provider.getService(this, requestor, serviceClass,
710                                                  serviceSelector);
711                    List childServices = (List) serviceUsers.get(child);
712                    if (childServices == null)
713                      {
714                        childServices = new ArrayList();
715                        serviceUsers.put(child, childServices);
716                      }
717                    childServices.add(serviceClass);
718                  }
719                else
720                  {
721                    BeanContextServices peer = getBeanContextServicesPeer();
722                    if (peer != null)
723                      service = peer.getService(child, requestor, serviceClass,
724                                                serviceSelector, bcsrl);
725                    else
726                      service = null;
727                  }
728                if (service != null)
729                  {
730                    ServiceRequest request = new ServiceRequest(requestor, bcsrl);
731                    Set requests = (Set) serviceRequests.get(serviceClass);
732                    if (requests == null)
733                      {
734                        requests = new HashSet();
735                        serviceRequests.put(serviceClass, requests);
736                      }
737                    requests.add(request);
738                    ServiceLease lease = new ServiceLease(requestor, service);
739                    serviceLeases.put(lease, provider);
740                  }
741                return service;
742              }
743          }
744      }
745    
746      /**
747       * Returns true if the specified service is available.
748       *
749       * @param serviceClass the service to check for.
750       * @return true if the service is available.
751       */
752      public boolean hasService (Class serviceClass)
753      {
754        synchronized (globalHierarchyLock)
755          {
756            synchronized (services)
757              {
758                return services.containsKey(serviceClass);
759              }
760          }
761      }
762    
763      public void initialize ()
764      {
765        super.initialize();
766    
767        bcsListeners = new ArrayList();
768        services = new HashMap();
769        serviceUsers = new HashMap();
770        serviceRequests = new HashMap();
771        serviceLeases = new HashMap();
772      }
773    
774      /**
775       * Subclasses may override this method to allocate resources
776       * from the nesting bean context.
777       */
778      protected  void initializeBeanContextResources()
779      {
780        /* Purposefully left empty */
781      }
782    
783      /**
784       * Relinquishes any resources obtained from the parent context.
785       * Specifically, those services obtained from the parent are revoked.
786       * Subclasses may override this method to deallocate resources
787       * from the nesting bean context.
788       */
789      protected void releaseBeanContextResources()
790      {
791        /* Purposefully left empty */
792      }
793    
794      /**
795       * Releases the reference to a service held by a
796       * {@link BeanContextChild} (or an arbitrary object associated
797       * with it).  It simply calls the appropriate method on the
798       * underlying provider.
799       *
800       * @param child the child who holds the reference.
801       * @param requestor the object that requested the reference.
802       * @param service the service being released.
803       */
804      public void releaseService (BeanContextChild child, Object requestor,
805                                  Object service)
806      {
807        synchronized (globalHierarchyLock)
808          {
809            synchronized (services)
810              {
811                ServiceLease lease = new ServiceLease(requestor, service);
812                BeanContextServiceProvider provider = (BeanContextServiceProvider)
813                  serviceLeases.get(lease);
814                if (provider != null)
815                  provider.releaseService(this, requestor, service);
816                else
817                  {
818                    BeanContextServices peer = getBeanContextServicesPeer();
819                    if (peer != null)
820                      peer.releaseService(child, requestor, service);
821                  }
822                serviceLeases.remove(lease);
823              }
824          }
825      }
826    
827      public void removeBeanContextServicesListener
828        (BeanContextServicesListener listener)
829      {
830        synchronized (bcsListeners)
831          {
832            bcsListeners.remove(listener);
833          }
834      }
835    
836      /**
837       * Revokes the given service.  A {@link BeanContextServiceRevokedEvent} is
838       * emitted to all registered {@link BeanContextServiceRevokedListener}s
839       * and {@link BeanContextServiceListener}s.  If <code>revokeCurrentServicesNow</code>
840       * is true, termination of the service is immediate.  Otherwise, prior
841       * acquisitions of the service by requestors remain valid.
842       *
843       * @param serviceClass the service to revoke.
844       * @param bcsp the provider of the revoked service.
845       * @param revokeCurrentServicesNow true if this is an exceptional circumstance
846       *                                 where service should be immediately revoked.
847       */
848      public void revokeService (Class serviceClass, BeanContextServiceProvider bcsp,
849                                 boolean revokeCurrentServicesNow)
850      {
851        synchronized (globalHierarchyLock)
852          {
853            synchronized (services)
854              {
855                fireServiceRevoked(serviceClass, revokeCurrentServicesNow);
856                services.remove(serviceClass);
857                if (bcsp instanceof Serializable)
858                  --serializable;
859              }
860          }
861      }
862    
863      public void serviceAvailable (BeanContextServiceAvailableEvent bcssae)
864      {
865        synchronized (services)
866          {
867            Class klass = bcssae.getServiceClass();
868            if (services.containsKey(klass))
869              return;
870            Iterator it = bcsChildren();
871            while (it.hasNext())
872              {
873                Object obj = it.next();
874                if (obj instanceof BeanContextServices)
875                  ((BeanContextServices) obj).serviceAvailable(bcssae);
876              }
877          }
878      }
879    
880      public void serviceRevoked (BeanContextServiceRevokedEvent bcssre)
881      {
882        synchronized (services)
883          {
884            Class klass = bcssre.getServiceClass();
885            if (services.containsKey(klass))
886              return;
887            Iterator it = bcsChildren();
888            while (it.hasNext())
889              {
890                Object obj = it.next();
891                if (obj instanceof BeanContextServices)
892                  ((BeanContextServices) obj).serviceRevoked(bcssre);
893              }
894          }
895      }
896    }