| /* |
| * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package java.beans.beancontext; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| |
| import java.io.IOException; |
| import java.io.ObjectInputStream; |
| import java.io.ObjectOutputStream; |
| import java.io.Serializable; |
| |
| import java.util.TooManyListenersException; |
| |
| import java.util.Locale; |
| |
| /** |
| * <p> |
| * This helper class provides a utility implementation of the |
| * java.beans.beancontext.BeanContextServices interface. |
| * </p> |
| * <p> |
| * Since this class directly implements the BeanContextServices interface, |
| * the class can, and is intended to be used either by subclassing this |
| * implementation, or via delegation of an instance of this class |
| * from another through the BeanContextProxy interface. |
| * </p> |
| * |
| * @author Laurence P. G. Cable |
| * @since 1.2 |
| */ |
| |
| public class BeanContextServicesSupport extends BeanContextSupport |
| implements BeanContextServices { |
| private static final long serialVersionUID = -8494482757288719206L; |
| |
| /** |
| * <p> |
| * Construct a BeanContextServicesSupport instance |
| * </p> |
| * |
| * @param peer The peer BeanContext we are supplying an implementation for, if null the this object is its own peer |
| * @param lcle The current Locale for this BeanContext. |
| * @param dTime The initial state, true if in design mode, false if runtime. |
| * @param visible The initial visibility. |
| * |
| */ |
| |
| public BeanContextServicesSupport(BeanContextServices peer, Locale lcle, boolean dTime, boolean visible) { |
| super(peer, lcle, dTime, visible); |
| } |
| |
| /** |
| * Create an instance using the specified Locale and design mode. |
| * |
| * @param peer The peer BeanContext we are supplying an implementation for, if null the this object is its own peer |
| * @param lcle The current Locale for this BeanContext. |
| * @param dtime The initial state, true if in design mode, false if runtime. |
| */ |
| |
| public BeanContextServicesSupport(BeanContextServices peer, Locale lcle, boolean dtime) { |
| this (peer, lcle, dtime, true); |
| } |
| |
| /** |
| * Create an instance using the specified locale |
| * |
| * @param peer The peer BeanContext we are supplying an implementation for, if null the this object is its own peer |
| * @param lcle The current Locale for this BeanContext. |
| */ |
| |
| public BeanContextServicesSupport(BeanContextServices peer, Locale lcle) { |
| this (peer, lcle, false, true); |
| } |
| |
| /** |
| * Create an instance with a peer |
| * |
| * @param peer The peer BeanContext we are supplying an implementation for, if null the this object is its own peer |
| */ |
| |
| public BeanContextServicesSupport(BeanContextServices peer) { |
| this (peer, null, false, true); |
| } |
| |
| /** |
| * Create an instance that is not a delegate of another object |
| */ |
| |
| public BeanContextServicesSupport() { |
| this (null, null, false, true); |
| } |
| |
| /** |
| * called by BeanContextSupport superclass during construction and |
| * deserialization to initialize subclass transient state. |
| * |
| * subclasses may envelope this method, but should not override it or |
| * call it directly. |
| */ |
| |
| public void initialize() { |
| super.initialize(); |
| services = new HashMap<>(serializable + 1); |
| bcsListeners = new ArrayList<>(1); |
| } |
| |
| /** |
| * Gets the {@code BeanContextServices} associated with this |
| * {@code BeanContextServicesSupport}. |
| * |
| * @return the instance of {@code BeanContext} |
| * this object is providing the implementation for. |
| */ |
| public BeanContextServices getBeanContextServicesPeer() { |
| return (BeanContextServices)getBeanContextChildPeer(); |
| } |
| |
| /************************************************************************/ |
| |
| /* |
| * protected nested class containing per child information, an instance |
| * of which is associated with each child in the "children" hashtable. |
| * subclasses can extend this class to include their own per-child state. |
| * |
| * Note that this 'value' is serialized with the corresponding child 'key' |
| * when the BeanContextSupport is serialized. |
| */ |
| |
| protected class BCSSChild extends BeanContextSupport.BCSChild { |
| |
| private static final long serialVersionUID = -3263851306889194873L; |
| |
| /* |
| * private nested class to map serviceClass to Provider and requestors |
| * listeners. |
| */ |
| |
| class BCSSCServiceClassRef { |
| |
| // create an instance of a service ref |
| |
| BCSSCServiceClassRef(Class<?> sc, BeanContextServiceProvider bcsp, boolean delegated) { |
| super(); |
| |
| serviceClass = sc; |
| |
| if (delegated) |
| delegateProvider = bcsp; |
| else |
| serviceProvider = bcsp; |
| } |
| |
| // add a requestor and assoc listener |
| |
| void addRequestor(Object requestor, BeanContextServiceRevokedListener bcsrl) throws TooManyListenersException { |
| BeanContextServiceRevokedListener cbcsrl = requestors.get(requestor); |
| |
| if (cbcsrl != null && !cbcsrl.equals(bcsrl)) |
| throw new TooManyListenersException(); |
| |
| requestors.put(requestor, bcsrl); |
| } |
| |
| // remove a requestor |
| |
| void removeRequestor(Object requestor) { |
| requestors.remove(requestor); |
| } |
| |
| // check a requestors listener |
| |
| void verifyRequestor(Object requestor, BeanContextServiceRevokedListener bcsrl) throws TooManyListenersException { |
| BeanContextServiceRevokedListener cbcsrl = requestors.get(requestor); |
| |
| if (cbcsrl != null && !cbcsrl.equals(bcsrl)) |
| throw new TooManyListenersException(); |
| } |
| |
| void verifyAndMaybeSetProvider(BeanContextServiceProvider bcsp, boolean isDelegated) { |
| BeanContextServiceProvider current; |
| |
| if (isDelegated) { // the provider is delegated |
| current = delegateProvider; |
| |
| if (current == null || bcsp == null) { |
| delegateProvider = bcsp; |
| return; |
| } |
| } else { // the provider is registered with this BCS |
| current = serviceProvider; |
| |
| if (current == null || bcsp == null) { |
| serviceProvider = bcsp; |
| return; |
| } |
| } |
| |
| if (!current.equals(bcsp)) |
| throw new UnsupportedOperationException("existing service reference obtained from different BeanContextServiceProvider not supported"); |
| |
| } |
| |
| @SuppressWarnings("unchecked") // Cast from clone |
| Iterator<Map.Entry<Object, BeanContextServiceRevokedListener>> cloneOfEntries() { |
| return ((HashMap<Object, BeanContextServiceRevokedListener>)requestors.clone()).entrySet().iterator(); |
| } |
| |
| Iterator<Map.Entry<Object, BeanContextServiceRevokedListener>> entries() { |
| return requestors.entrySet().iterator(); |
| } |
| |
| boolean isEmpty() { return requestors.isEmpty(); } |
| |
| Class<?> getServiceClass() { return serviceClass; } |
| |
| BeanContextServiceProvider getServiceProvider() { |
| return serviceProvider; |
| } |
| |
| BeanContextServiceProvider getDelegateProvider() { |
| return delegateProvider; |
| } |
| |
| boolean isDelegated() { return delegateProvider != null; } |
| |
| void addRef(boolean delegated) { |
| if (delegated) { |
| delegateRefs++; |
| } else { |
| serviceRefs++; |
| } |
| } |
| |
| |
| void releaseRef(boolean delegated) { |
| if (delegated) { |
| if (--delegateRefs == 0) { |
| delegateProvider = null; |
| } |
| } else { |
| if (--serviceRefs <= 0) { |
| serviceProvider = null; |
| } |
| } |
| } |
| |
| int getRefs() { return serviceRefs + delegateRefs; } |
| |
| int getDelegateRefs() { return delegateRefs; } |
| |
| int getServiceRefs() { return serviceRefs; } |
| |
| /* |
| * fields |
| */ |
| |
| Class<?> serviceClass; |
| |
| BeanContextServiceProvider serviceProvider; |
| int serviceRefs; |
| |
| BeanContextServiceProvider delegateProvider; // proxy |
| int delegateRefs; |
| |
| HashMap<Object, BeanContextServiceRevokedListener> requestors = new HashMap<>(1); |
| } |
| |
| /* |
| * per service reference info ... |
| */ |
| |
| class BCSSCServiceRef { |
| BCSSCServiceRef(BCSSCServiceClassRef scref, boolean isDelegated) { |
| serviceClassRef = scref; |
| delegated = isDelegated; |
| } |
| |
| void addRef() { refCnt++; } |
| int release() { return --refCnt; } |
| |
| BCSSCServiceClassRef getServiceClassRef() { return serviceClassRef; } |
| |
| boolean isDelegated() { return delegated; } |
| |
| /* |
| * fields |
| */ |
| |
| BCSSCServiceClassRef serviceClassRef; |
| int refCnt = 1; |
| boolean delegated = false; |
| } |
| |
| BCSSChild(Object bcc, Object peer) { super(bcc, peer); } |
| |
| // note usage of service per requestor, per service |
| |
| synchronized void usingService(Object requestor, Object service, Class<?> serviceClass, BeanContextServiceProvider bcsp, boolean isDelegated, BeanContextServiceRevokedListener bcsrl) throws TooManyListenersException, UnsupportedOperationException { |
| |
| // first, process mapping from serviceClass to requestor(s) |
| |
| BCSSCServiceClassRef serviceClassRef = null; |
| |
| if (serviceClasses == null) |
| serviceClasses = new HashMap<>(1); |
| else |
| serviceClassRef = serviceClasses.get(serviceClass); |
| |
| if (serviceClassRef == null) { // new service being used ... |
| serviceClassRef = new BCSSCServiceClassRef(serviceClass, bcsp, isDelegated); |
| serviceClasses.put(serviceClass, serviceClassRef); |
| |
| } else { // existing service ... |
| serviceClassRef.verifyAndMaybeSetProvider(bcsp, isDelegated); // throws |
| serviceClassRef.verifyRequestor(requestor, bcsrl); // throws |
| } |
| |
| serviceClassRef.addRequestor(requestor, bcsrl); |
| serviceClassRef.addRef(isDelegated); |
| |
| // now handle mapping from requestor to service(s) |
| |
| BCSSCServiceRef serviceRef = null; |
| Map<Object , BCSSCServiceRef> services = null; |
| |
| if (serviceRequestors == null) { |
| serviceRequestors = new HashMap<>(1); |
| } else { |
| services = serviceRequestors.get(requestor); |
| } |
| |
| if (services == null) { |
| services = new HashMap<>(1); |
| |
| serviceRequestors.put(requestor, services); |
| } else |
| serviceRef = services.get(service); |
| |
| if (serviceRef == null) { |
| serviceRef = new BCSSCServiceRef(serviceClassRef, isDelegated); |
| |
| services.put(service, serviceRef); |
| } else { |
| serviceRef.addRef(); |
| } |
| } |
| |
| // release a service reference |
| |
| synchronized void releaseService(Object requestor, Object service) { |
| if (serviceRequestors == null) return; |
| |
| Map<Object, BCSSCServiceRef> services = serviceRequestors.get(requestor); |
| |
| if (services == null) return; // oops its not there anymore! |
| |
| BCSSCServiceRef serviceRef = services.get(service); |
| |
| if (serviceRef == null) return; // oops its not there anymore! |
| |
| BCSSCServiceClassRef serviceClassRef = serviceRef.getServiceClassRef(); |
| boolean isDelegated = serviceRef.isDelegated(); |
| BeanContextServiceProvider bcsp = isDelegated ? serviceClassRef.getDelegateProvider() : serviceClassRef.getServiceProvider(); |
| |
| bcsp.releaseService(BeanContextServicesSupport.this.getBeanContextServicesPeer(), requestor, service); |
| |
| serviceClassRef.releaseRef(isDelegated); |
| serviceClassRef.removeRequestor(requestor); |
| |
| if (serviceRef.release() == 0) { |
| |
| services.remove(service); |
| |
| if (services.isEmpty()) { |
| serviceRequestors.remove(requestor); |
| serviceClassRef.removeRequestor(requestor); |
| } |
| |
| if (serviceRequestors.isEmpty()) { |
| serviceRequestors = null; |
| } |
| |
| if (serviceClassRef.isEmpty()) { |
| serviceClasses.remove(serviceClassRef.getServiceClass()); |
| } |
| |
| if (serviceClasses.isEmpty()) |
| serviceClasses = null; |
| } |
| } |
| |
| // revoke a service |
| |
| synchronized void revokeService(Class<?> serviceClass, boolean isDelegated, boolean revokeNow) { |
| if (serviceClasses == null) return; |
| |
| BCSSCServiceClassRef serviceClassRef = serviceClasses.get(serviceClass); |
| |
| if (serviceClassRef == null) return; |
| |
| Iterator<Map.Entry<Object, BeanContextServiceRevokedListener>> i = serviceClassRef.cloneOfEntries(); |
| |
| BeanContextServiceRevokedEvent bcsre = new BeanContextServiceRevokedEvent(BeanContextServicesSupport.this.getBeanContextServicesPeer(), serviceClass, revokeNow); |
| boolean noMoreRefs = false; |
| |
| while (i.hasNext() && serviceRequestors != null) { |
| Map.Entry<Object,BeanContextServiceRevokedListener> entry = i.next(); |
| BeanContextServiceRevokedListener listener = entry.getValue(); |
| |
| if (revokeNow) { |
| Object requestor = entry.getKey(); |
| Map<Object, BCSSCServiceRef> services = serviceRequestors.get(requestor); |
| |
| if (services != null) { |
| Iterator<Map.Entry<Object, BCSSCServiceRef>> i1 = services.entrySet().iterator(); |
| |
| while (i1.hasNext()) { |
| Map.Entry<Object, BCSSCServiceRef> tmp = i1.next(); |
| |
| BCSSCServiceRef serviceRef = tmp.getValue(); |
| if (serviceRef.getServiceClassRef().equals(serviceClassRef) && isDelegated == serviceRef.isDelegated()) { |
| i1.remove(); |
| } |
| } |
| |
| if (noMoreRefs = services.isEmpty()) { |
| serviceRequestors.remove(requestor); |
| } |
| } |
| |
| if (noMoreRefs) serviceClassRef.removeRequestor(requestor); |
| } |
| |
| listener.serviceRevoked(bcsre); |
| } |
| |
| if (revokeNow && serviceClasses != null) { |
| if (serviceClassRef.isEmpty()) |
| serviceClasses.remove(serviceClass); |
| |
| if (serviceClasses.isEmpty()) |
| serviceClasses = null; |
| } |
| |
| if (serviceRequestors != null && serviceRequestors.isEmpty()) |
| serviceRequestors = null; |
| } |
| |
| // release all references for this child since it has been unnested. |
| |
| void cleanupReferences() { |
| |
| if (serviceRequestors == null) return; |
| |
| Iterator<Map.Entry<Object, Map<Object, BCSSCServiceRef>>> requestors = serviceRequestors.entrySet().iterator(); |
| |
| while(requestors.hasNext()) { |
| Map.Entry<Object, Map<Object, BCSSCServiceRef>> tmp = requestors.next(); |
| Object requestor = tmp.getKey(); |
| Iterator<Map.Entry<Object, BCSSCServiceRef>> services = tmp.getValue().entrySet().iterator(); |
| |
| requestors.remove(); |
| |
| while (services.hasNext()) { |
| Map.Entry<Object, BCSSCServiceRef> entry = services.next(); |
| Object service = entry.getKey(); |
| BCSSCServiceRef sref = entry.getValue(); |
| |
| BCSSCServiceClassRef scref = sref.getServiceClassRef(); |
| |
| BeanContextServiceProvider bcsp = sref.isDelegated() ? scref.getDelegateProvider() : scref.getServiceProvider(); |
| |
| scref.removeRequestor(requestor); |
| services.remove(); |
| |
| while (sref.release() >= 0) { |
| bcsp.releaseService(BeanContextServicesSupport.this.getBeanContextServicesPeer(), requestor, service); |
| } |
| } |
| } |
| |
| serviceRequestors = null; |
| serviceClasses = null; |
| } |
| |
| void revokeAllDelegatedServicesNow() { |
| if (serviceClasses == null) return; |
| |
| Iterator<BCSSCServiceClassRef> serviceClassRefs = |
| new HashSet<>(serviceClasses.values()).iterator(); |
| |
| while (serviceClassRefs.hasNext()) { |
| BCSSCServiceClassRef serviceClassRef = serviceClassRefs.next(); |
| |
| if (!serviceClassRef.isDelegated()) continue; |
| |
| Iterator<Map.Entry<Object, BeanContextServiceRevokedListener>> i = serviceClassRef.cloneOfEntries(); |
| BeanContextServiceRevokedEvent bcsre = new BeanContextServiceRevokedEvent(BeanContextServicesSupport.this.getBeanContextServicesPeer(), serviceClassRef.getServiceClass(), true); |
| boolean noMoreRefs = false; |
| |
| while (i.hasNext()) { |
| Map.Entry<Object, BeanContextServiceRevokedListener> entry = i.next(); |
| BeanContextServiceRevokedListener listener = entry.getValue(); |
| |
| Object requestor = entry.getKey(); |
| Map<Object, BCSSCServiceRef> services = serviceRequestors.get(requestor); |
| |
| if (services != null) { |
| Iterator<Map.Entry<Object, BCSSCServiceRef>> i1 = services.entrySet().iterator(); |
| |
| while (i1.hasNext()) { |
| Map.Entry<Object, BCSSCServiceRef> tmp = i1.next(); |
| |
| BCSSCServiceRef serviceRef = tmp.getValue(); |
| if (serviceRef.getServiceClassRef().equals(serviceClassRef) && serviceRef.isDelegated()) { |
| i1.remove(); |
| } |
| } |
| |
| if (noMoreRefs = services.isEmpty()) { |
| serviceRequestors.remove(requestor); |
| } |
| } |
| |
| if (noMoreRefs) serviceClassRef.removeRequestor(requestor); |
| |
| listener.serviceRevoked(bcsre); |
| |
| if (serviceClassRef.isEmpty()) |
| serviceClasses.remove(serviceClassRef.getServiceClass()); |
| } |
| } |
| |
| if (serviceClasses.isEmpty()) serviceClasses = null; |
| |
| if (serviceRequestors != null && serviceRequestors.isEmpty()) |
| serviceRequestors = null; |
| } |
| |
| /* |
| * fields |
| */ |
| |
| private transient HashMap<Class<?>, BCSSCServiceClassRef> serviceClasses; |
| private transient HashMap<Object, Map<Object, BeanContextServicesSupport.BCSSChild.BCSSCServiceRef>> serviceRequestors; |
| } |
| |
| /** |
| * <p> |
| * Subclasses can override this method to insert their own subclass |
| * of Child without having to override add() or the other Collection |
| * methods that add children to the set. |
| * </p> |
| * |
| * @param targetChild the child to create the Child on behalf of |
| * @param peer the peer if the targetChild and peer are related by BeanContextProxy |
| */ |
| |
| protected BCSChild createBCSChild(Object targetChild, Object peer) { |
| return new BCSSChild(targetChild, peer); |
| } |
| |
| /************************************************************************/ |
| |
| /** |
| * subclasses may subclass this nested class to add behaviors for |
| * each BeanContextServicesProvider. |
| */ |
| |
| protected static class BCSSServiceProvider implements Serializable { |
| private static final long serialVersionUID = 861278251667444782L; |
| |
| BCSSServiceProvider(Class<?> sc, BeanContextServiceProvider bcsp) { |
| super(); |
| |
| serviceProvider = bcsp; |
| } |
| |
| /** |
| * Returns the service provider. |
| * @return the service provider |
| */ |
| protected BeanContextServiceProvider getServiceProvider() { |
| return serviceProvider; |
| } |
| |
| /** |
| * The service provider. |
| */ |
| |
| protected BeanContextServiceProvider serviceProvider; |
| } |
| |
| /** |
| * subclasses can override this method to create new subclasses of |
| * BCSSServiceProvider without having to override addService() in |
| * order to instantiate. |
| * @param sc the class |
| * @param bcsp the service provider |
| * @return a service provider without overriding addService() |
| */ |
| |
| protected BCSSServiceProvider createBCSSServiceProvider(Class<?> sc, BeanContextServiceProvider bcsp) { |
| return new BCSSServiceProvider(sc, bcsp); |
| } |
| |
| /************************************************************************/ |
| |
| /** |
| * add a BeanContextServicesListener |
| * |
| * @throws NullPointerException if the argument is null |
| */ |
| |
| public void addBeanContextServicesListener(BeanContextServicesListener bcsl) { |
| if (bcsl == null) throw new NullPointerException("bcsl"); |
| |
| synchronized(bcsListeners) { |
| if (bcsListeners.contains(bcsl)) |
| return; |
| else |
| bcsListeners.add(bcsl); |
| } |
| } |
| |
| /** |
| * remove a BeanContextServicesListener |
| */ |
| |
| public void removeBeanContextServicesListener(BeanContextServicesListener bcsl) { |
| if (bcsl == null) throw new NullPointerException("bcsl"); |
| |
| synchronized(bcsListeners) { |
| if (!bcsListeners.contains(bcsl)) |
| return; |
| else |
| bcsListeners.remove(bcsl); |
| } |
| } |
| |
| /** |
| * add a service |
| * @param serviceClass the service class |
| * @param bcsp the service provider |
| */ |
| |
| public boolean addService(Class<?> serviceClass, BeanContextServiceProvider bcsp) { |
| return addService(serviceClass, bcsp, true); |
| } |
| |
| /** |
| * add a service |
| * @param serviceClass the service class |
| * @param bcsp the service provider |
| * @param fireEvent whether or not an event should be fired |
| * @return true if the service was successfully added |
| */ |
| |
| protected boolean addService(Class<?> serviceClass, BeanContextServiceProvider bcsp, boolean fireEvent) { |
| |
| if (serviceClass == null) throw new NullPointerException("serviceClass"); |
| if (bcsp == null) throw new NullPointerException("bcsp"); |
| |
| synchronized(BeanContext.globalHierarchyLock) { |
| if (services.containsKey(serviceClass)) |
| return false; |
| else { |
| services.put(serviceClass, createBCSSServiceProvider(serviceClass, bcsp)); |
| |
| if (bcsp instanceof Serializable) serializable++; |
| |
| if (!fireEvent) return true; |
| |
| |
| BeanContextServiceAvailableEvent bcssae = new BeanContextServiceAvailableEvent(getBeanContextServicesPeer(), serviceClass); |
| |
| fireServiceAdded(bcssae); |
| |
| synchronized(children) { |
| Iterator<Object> i = children.keySet().iterator(); |
| |
| while (i.hasNext()) { |
| Object c = i.next(); |
| |
| if (c instanceof BeanContextServices) { |
| ((BeanContextServicesListener)c).serviceAvailable(bcssae); |
| } |
| } |
| } |
| |
| return true; |
| } |
| } |
| } |
| |
| /** |
| * remove a service |
| * @param serviceClass the service class |
| * @param bcsp the service provider |
| * @param revokeCurrentServicesNow whether or not to revoke the service |
| */ |
| |
| public void revokeService(Class<?> serviceClass, BeanContextServiceProvider bcsp, boolean revokeCurrentServicesNow) { |
| |
| if (serviceClass == null) throw new NullPointerException("serviceClass"); |
| if (bcsp == null) throw new NullPointerException("bcsp"); |
| |
| synchronized(BeanContext.globalHierarchyLock) { |
| if (!services.containsKey(serviceClass)) return; |
| |
| BCSSServiceProvider bcsssp = services.get(serviceClass); |
| |
| if (!bcsssp.getServiceProvider().equals(bcsp)) |
| throw new IllegalArgumentException("service provider mismatch"); |
| |
| services.remove(serviceClass); |
| |
| if (bcsp instanceof Serializable) serializable--; |
| |
| Iterator<BeanContextSupport.BCSChild> i = bcsChildren(); // get the BCSChild values. |
| |
| while (i.hasNext()) { |
| ((BCSSChild)i.next()).revokeService(serviceClass, false, revokeCurrentServicesNow); |
| } |
| |
| fireServiceRevoked(serviceClass, revokeCurrentServicesNow); |
| } |
| } |
| |
| /** |
| * has a service, which may be delegated |
| */ |
| |
| public synchronized boolean hasService(Class<?> serviceClass) { |
| if (serviceClass == null) throw new NullPointerException("serviceClass"); |
| |
| synchronized(BeanContext.globalHierarchyLock) { |
| if (services.containsKey(serviceClass)) return true; |
| |
| BeanContextServices bcs = null; |
| |
| try { |
| bcs = (BeanContextServices)getBeanContext(); |
| } catch (ClassCastException cce) { |
| return false; |
| } |
| |
| return bcs == null ? false : bcs.hasService(serviceClass); |
| } |
| } |
| |
| /************************************************************************/ |
| |
| /* |
| * a nested subclass used to represent a proxy for serviceClasses delegated |
| * to an enclosing BeanContext. |
| */ |
| |
| protected class BCSSProxyServiceProvider implements BeanContextServiceProvider, BeanContextServiceRevokedListener { |
| |
| BCSSProxyServiceProvider(BeanContextServices bcs) { |
| super(); |
| |
| nestingCtxt = bcs; |
| } |
| |
| public Object getService(BeanContextServices bcs, Object requestor, Class<?> serviceClass, Object serviceSelector) { |
| Object service = null; |
| |
| try { |
| service = nestingCtxt.getService(bcs, requestor, serviceClass, serviceSelector, this); |
| } catch (TooManyListenersException tmle) { |
| return null; |
| } |
| |
| return service; |
| } |
| |
| public void releaseService(BeanContextServices bcs, Object requestor, Object service) { |
| nestingCtxt.releaseService(bcs, requestor, service); |
| } |
| |
| public Iterator<?> getCurrentServiceSelectors(BeanContextServices bcs, Class<?> serviceClass) { |
| return nestingCtxt.getCurrentServiceSelectors(serviceClass); |
| } |
| |
| public void serviceRevoked(BeanContextServiceRevokedEvent bcsre) { |
| Iterator<BeanContextSupport.BCSChild> i = bcsChildren(); // get the BCSChild values. |
| |
| while (i.hasNext()) { |
| ((BCSSChild)i.next()).revokeService(bcsre.getServiceClass(), true, bcsre.isCurrentServiceInvalidNow()); |
| } |
| } |
| |
| /* |
| * fields |
| */ |
| |
| private BeanContextServices nestingCtxt; |
| } |
| |
| /************************************************************************/ |
| |
| /** |
| * obtain a service which may be delegated |
| */ |
| |
| public Object getService(BeanContextChild child, Object requestor, Class<?> serviceClass, Object serviceSelector, BeanContextServiceRevokedListener bcsrl) throws TooManyListenersException { |
| if (child == null) throw new NullPointerException("child"); |
| if (serviceClass == null) throw new NullPointerException("serviceClass"); |
| if (requestor == null) throw new NullPointerException("requestor"); |
| if (bcsrl == null) throw new NullPointerException("bcsrl"); |
| |
| Object service = null; |
| BCSSChild bcsc; |
| BeanContextServices bcssp = getBeanContextServicesPeer(); |
| |
| synchronized(BeanContext.globalHierarchyLock) { |
| synchronized(children) { bcsc = (BCSSChild)children.get(child); } |
| |
| if (bcsc == null) throw new IllegalArgumentException("not a child of this context"); // not a child ... |
| |
| BCSSServiceProvider bcsssp = services.get(serviceClass); |
| |
| if (bcsssp != null) { |
| BeanContextServiceProvider bcsp = bcsssp.getServiceProvider(); |
| service = bcsp.getService(bcssp, requestor, serviceClass, serviceSelector); |
| if (service != null) { // do bookkeeping ... |
| try { |
| bcsc.usingService(requestor, service, serviceClass, bcsp, false, bcsrl); |
| } catch (TooManyListenersException tmle) { |
| bcsp.releaseService(bcssp, requestor, service); |
| throw tmle; |
| } catch (UnsupportedOperationException uope) { |
| bcsp.releaseService(bcssp, requestor, service); |
| throw uope; // unchecked rt exception |
| } |
| |
| return service; |
| } |
| } |
| |
| |
| if (proxy != null) { |
| |
| // try to delegate ... |
| |
| service = proxy.getService(bcssp, requestor, serviceClass, serviceSelector); |
| |
| if (service != null) { // do bookkeeping ... |
| try { |
| bcsc.usingService(requestor, service, serviceClass, proxy, true, bcsrl); |
| } catch (TooManyListenersException tmle) { |
| proxy.releaseService(bcssp, requestor, service); |
| throw tmle; |
| } catch (UnsupportedOperationException uope) { |
| proxy.releaseService(bcssp, requestor, service); |
| throw uope; // unchecked rt exception |
| } |
| |
| return service; |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * release a service |
| */ |
| |
| public void releaseService(BeanContextChild child, Object requestor, Object service) { |
| if (child == null) throw new NullPointerException("child"); |
| if (requestor == null) throw new NullPointerException("requestor"); |
| if (service == null) throw new NullPointerException("service"); |
| |
| BCSSChild bcsc; |
| |
| synchronized(BeanContext.globalHierarchyLock) { |
| synchronized(children) { bcsc = (BCSSChild)children.get(child); } |
| |
| if (bcsc != null) |
| bcsc.releaseService(requestor, service); |
| else |
| throw new IllegalArgumentException("child actual is not a child of this BeanContext"); |
| } |
| } |
| |
| /** |
| * @return an iterator for all the currently registered service classes. |
| */ |
| |
| public Iterator<Object> getCurrentServiceClasses() { |
| return new BCSIterator(services.keySet().iterator()); |
| } |
| |
| /** |
| * @return an iterator for all the currently available service selectors |
| * (if any) available for the specified service. |
| */ |
| |
| public Iterator<?> getCurrentServiceSelectors(Class<?> serviceClass) { |
| |
| BCSSServiceProvider bcsssp = services.get(serviceClass); |
| |
| return bcsssp != null ? new BCSIterator(bcsssp.getServiceProvider().getCurrentServiceSelectors(getBeanContextServicesPeer(), serviceClass)) : null; |
| } |
| |
| /** |
| * BeanContextServicesListener callback, propagates event to all |
| * currently registered listeners and BeanContextServices children, |
| * if this BeanContextService does not already implement this service |
| * itself. |
| * |
| * subclasses may override or envelope this method to implement their |
| * own propagation semantics. |
| */ |
| |
| public void serviceAvailable(BeanContextServiceAvailableEvent bcssae) { |
| synchronized(BeanContext.globalHierarchyLock) { |
| if (services.containsKey(bcssae.getServiceClass())) return; |
| |
| fireServiceAdded(bcssae); |
| |
| Iterator<Object> i; |
| |
| synchronized(children) { |
| i = children.keySet().iterator(); |
| } |
| |
| while (i.hasNext()) { |
| Object c = i.next(); |
| |
| if (c instanceof BeanContextServices) { |
| ((BeanContextServicesListener)c).serviceAvailable(bcssae); |
| } |
| } |
| } |
| } |
| |
| /** |
| * BeanContextServicesListener callback, propagates event to all |
| * currently registered listeners and BeanContextServices children, |
| * if this BeanContextService does not already implement this service |
| * itself. |
| * |
| * subclasses may override or envelope this method to implement their |
| * own propagation semantics. |
| */ |
| |
| public void serviceRevoked(BeanContextServiceRevokedEvent bcssre) { |
| synchronized(BeanContext.globalHierarchyLock) { |
| if (services.containsKey(bcssre.getServiceClass())) return; |
| |
| fireServiceRevoked(bcssre); |
| |
| Iterator<Object> i; |
| |
| synchronized(children) { |
| i = children.keySet().iterator(); |
| } |
| |
| while (i.hasNext()) { |
| Object c = i.next(); |
| |
| if (c instanceof BeanContextServices) { |
| ((BeanContextServicesListener)c).serviceRevoked(bcssre); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Gets the {@code BeanContextServicesListener} (if any) of the specified |
| * child. |
| * |
| * @param child the specified child |
| * @return the BeanContextServicesListener (if any) of the specified child |
| */ |
| protected static final BeanContextServicesListener getChildBeanContextServicesListener(Object child) { |
| try { |
| return (BeanContextServicesListener)child; |
| } catch (ClassCastException cce) { |
| return null; |
| } |
| } |
| |
| /** |
| * called from superclass child removal operations after a child |
| * has been successfully removed. called with child synchronized. |
| * |
| * This subclass uses this hook to immediately revoke any services |
| * being used by this child if it is a BeanContextChild. |
| * |
| * subclasses may envelope this method in order to implement their |
| * own child removal side-effects. |
| */ |
| |
| protected void childJustRemovedHook(Object child, BCSChild bcsc) { |
| BCSSChild bcssc = (BCSSChild)bcsc; |
| |
| bcssc.cleanupReferences(); |
| } |
| |
| /** |
| * called from setBeanContext to notify a BeanContextChild |
| * to release resources obtained from the nesting BeanContext. |
| * |
| * This method revokes any services obtained from its parent. |
| * |
| * subclasses may envelope this method to implement their own semantics. |
| */ |
| |
| protected synchronized void releaseBeanContextResources() { |
| Object[] bcssc; |
| |
| super.releaseBeanContextResources(); |
| |
| synchronized(children) { |
| if (children.isEmpty()) return; |
| |
| bcssc = children.values().toArray(); |
| } |
| |
| |
| for (int i = 0; i < bcssc.length; i++) { |
| ((BCSSChild)bcssc[i]).revokeAllDelegatedServicesNow(); |
| } |
| |
| proxy = null; |
| } |
| |
| /** |
| * called from setBeanContext to notify a BeanContextChild |
| * to allocate resources obtained from the nesting BeanContext. |
| * |
| * subclasses may envelope this method to implement their own semantics. |
| */ |
| |
| protected synchronized void initializeBeanContextResources() { |
| super.initializeBeanContextResources(); |
| |
| BeanContext nbc = getBeanContext(); |
| |
| if (nbc == null) return; |
| |
| try { |
| BeanContextServices bcs = (BeanContextServices)nbc; |
| |
| proxy = new BCSSProxyServiceProvider(bcs); |
| } catch (ClassCastException cce) { |
| // do nothing ... |
| } |
| } |
| |
| /** |
| * Fires a {@code BeanContextServiceEvent} notifying of a new service. |
| * @param serviceClass the service class |
| */ |
| protected final void fireServiceAdded(Class<?> serviceClass) { |
| BeanContextServiceAvailableEvent bcssae = new BeanContextServiceAvailableEvent(getBeanContextServicesPeer(), serviceClass); |
| |
| fireServiceAdded(bcssae); |
| } |
| |
| /** |
| * Fires a {@code BeanContextServiceAvailableEvent} indicating that a new |
| * service has become available. |
| * |
| * @param bcssae the {@code BeanContextServiceAvailableEvent} |
| */ |
| protected final void fireServiceAdded(BeanContextServiceAvailableEvent bcssae) { |
| Object[] copy; |
| |
| synchronized (bcsListeners) { copy = bcsListeners.toArray(); } |
| |
| for (int i = 0; i < copy.length; i++) { |
| ((BeanContextServicesListener)copy[i]).serviceAvailable(bcssae); |
| } |
| } |
| |
| /** |
| * Fires a {@code BeanContextServiceEvent} notifying of a service being revoked. |
| * |
| * @param bcsre the {@code BeanContextServiceRevokedEvent} |
| */ |
| protected final void fireServiceRevoked(BeanContextServiceRevokedEvent bcsre) { |
| Object[] copy; |
| |
| synchronized (bcsListeners) { copy = bcsListeners.toArray(); } |
| |
| for (int i = 0; i < copy.length; i++) { |
| ((BeanContextServiceRevokedListener)copy[i]).serviceRevoked(bcsre); |
| } |
| } |
| |
| /** |
| * Fires a {@code BeanContextServiceRevokedEvent} |
| * indicating that a particular service is |
| * no longer available. |
| * @param serviceClass the service class |
| * @param revokeNow whether or not the event should be revoked now |
| */ |
| protected final void fireServiceRevoked(Class<?> serviceClass, boolean revokeNow) { |
| Object[] copy; |
| BeanContextServiceRevokedEvent bcsre = new BeanContextServiceRevokedEvent(getBeanContextServicesPeer(), serviceClass, revokeNow); |
| |
| synchronized (bcsListeners) { copy = bcsListeners.toArray(); } |
| |
| for (int i = 0; i < copy.length; i++) { |
| ((BeanContextServicesListener)copy[i]).serviceRevoked(bcsre); |
| } |
| } |
| |
| /** |
| * called from BeanContextSupport writeObject before it serializes the |
| * children ... |
| * |
| * This class will serialize any Serializable BeanContextServiceProviders |
| * herein. |
| * |
| * subclasses may envelope this method to insert their own serialization |
| * processing that has to occur prior to serialization of the children |
| */ |
| |
| protected synchronized void bcsPreSerializationHook(ObjectOutputStream oos) throws IOException { |
| |
| oos.writeInt(serializable); |
| |
| if (serializable <= 0) return; |
| |
| int count = 0; |
| |
| Iterator<Map.Entry<Object, BCSSServiceProvider>> i = services.entrySet().iterator(); |
| |
| while (i.hasNext() && count < serializable) { |
| Map.Entry<Object, BCSSServiceProvider> entry = i.next(); |
| BCSSServiceProvider bcsp = null; |
| |
| try { |
| bcsp = entry.getValue(); |
| } catch (ClassCastException cce) { |
| continue; |
| } |
| |
| if (bcsp.getServiceProvider() instanceof Serializable) { |
| oos.writeObject(entry.getKey()); |
| oos.writeObject(bcsp); |
| count++; |
| } |
| } |
| |
| if (count != serializable) |
| throw new IOException("wrote different number of service providers than expected"); |
| } |
| |
| /** |
| * called from BeanContextSupport readObject before it deserializes the |
| * children ... |
| * |
| * This class will deserialize any Serializable BeanContextServiceProviders |
| * serialized earlier thus making them available to the children when they |
| * deserialized. |
| * |
| * subclasses may envelope this method to insert their own serialization |
| * processing that has to occur prior to serialization of the children |
| */ |
| |
| protected synchronized void bcsPreDeserializationHook(ObjectInputStream ois) throws IOException, ClassNotFoundException { |
| |
| serializable = ois.readInt(); |
| |
| int count = serializable; |
| |
| while (count > 0) { |
| services.put(ois.readObject(), (BCSSServiceProvider)ois.readObject()); |
| count--; |
| } |
| } |
| |
| /** |
| * serialize the instance |
| */ |
| |
| private synchronized void writeObject(ObjectOutputStream oos) throws IOException { |
| oos.defaultWriteObject(); |
| |
| serialize(oos, (Collection)bcsListeners); |
| } |
| |
| /** |
| * deserialize the instance |
| */ |
| |
| private synchronized void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { |
| |
| ois.defaultReadObject(); |
| |
| deserialize(ois, (Collection)bcsListeners); |
| } |
| |
| |
| /* |
| * fields |
| */ |
| |
| /** |
| * all accesses to the {@code protected transient HashMap services} |
| * field should be synchronized on that object |
| */ |
| protected transient HashMap<Object, BCSSServiceProvider> services; |
| |
| /** |
| * The number of instances of a serializable {@code BeanContextServceProvider}. |
| */ |
| protected transient int serializable = 0; |
| |
| |
| /** |
| * Delegate for the {@code BeanContextServiceProvider}. |
| */ |
| protected transient BCSSProxyServiceProvider proxy; |
| |
| |
| /** |
| * List of {@code BeanContextServicesListener} objects. |
| */ |
| protected transient ArrayList<BeanContextServicesListener> bcsListeners; |
| } |