Package org.rioproject.impl.client

Source Code of org.rioproject.impl.client.LookupCachePool$ContainerListener

/*
* Copyright to the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.rioproject.impl.client;

import com.sun.jini.lookup.entry.LookupAttributes;
import net.jini.admin.Administrable;
import net.jini.admin.JoinAdmin;
import net.jini.config.Configuration;
import net.jini.config.ConfigurationException;
import net.jini.config.EmptyConfiguration;
import net.jini.core.discovery.LookupLocator;
import net.jini.core.entry.Entry;
import net.jini.core.lookup.ServiceID;
import net.jini.core.lookup.ServiceItem;
import net.jini.core.lookup.ServiceTemplate;
import net.jini.discovery.DiscoveryManagement;
import net.jini.discovery.LookupDiscovery;
import net.jini.id.Uuid;
import net.jini.lease.LeaseRenewalManager;
import net.jini.lookup.*;
import net.jini.lookup.entry.Name;
import org.rioproject.deploy.ServiceBeanInstance;
import org.rioproject.deploy.ServiceRecord;
import org.rioproject.impl.container.ServiceBeanContainer;
import org.rioproject.impl.container.ServiceBeanContainerListener;
import org.rioproject.impl.container.ServiceBeanDelegate;
import org.rioproject.impl.opstring.OpStringFilter;
import org.rioproject.opstring.ClassBundle;
import org.rioproject.servicecore.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.rmi.RemoteException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
* The LookupCachePool class provides the support to get an existing
* LookupCache from a pool of created LookupCache instances. Criteria for
* determining LookupCache matching is based on ServiceTemplate matching
*
* @author Dennis Reedy
*/
public class LookupCachePool {
    private final List<SDMWrapper> pool = new ArrayList<SDMWrapper>();
    private ServiceBeanContainerListener containerListener;
    private static final String COMPONENT = LookupCachePool.class.getName();
    private static final Logger logger = LoggerFactory.getLogger(COMPONENT);
    private static Configuration config;
    private static LookupCachePool singleton = new LookupCachePool();

    private LookupCachePool() {
        logger.debug("Create new LookupCachePool");
    }

    /**
     * Get the singleton instance of the LookupCachePool
     *
     * @return An instance of the LookupCachePool
     */
    public static synchronized LookupCachePool getInstance() {
        return(singleton);
    }

    /**
     * Set the {@link org.rioproject.impl.container.ServiceBeanContainer}
     *
     * @param container The <tt><ServiceBeanContainer/tt> used for local
     * discovery
     */
    public void setServiceBeanContainer(final ServiceBeanContainer container) {
        if(container !=null && containerListener==null) {
            containerListener = new ContainerListener(container);
        }
    }

    /**
     * Set the Configuration property
     *
     * @param conf The Configuration to use when creating
     * ServiceDiscoveryManager instances
     */
    public void setConfiguration(final Configuration conf) {
        config = conf;
        logger.debug("Set configuration for LookupCachePool {}", config);
    }
   
    /**
     * This method will return an instance of LookupCache based on matching the
     * DiscoveryManagement instance and ServiceTemplate provided as
     * criteria. If there is an existing LookupCache instance created by a
     * ServiceDiscoveryManager instance this utility has created that matches the
     * supplied criteria, that instance will be returned.
     *
     * <p>If the LookupCache can not be found due to not being able to match discovery
     * criteria to a known ServiceDiscoveryManager instance a new
     * ServiceDiscoveryManager instance will be created, then a LookupCache instance
     * created and returned.
     *
     * <p>If a ServiceDiscoveryManager can be matched, but not a LookupCache, a new
     * LookupCache will be created using the matched ServiceDiscoveryManager
     *
     * @param dMgr A DiscoveryManager instance created by the DiscoveryManagementPool
     * @param template ServiceTemplate to match
     * @return A ServiceDiscoveryManager object based on the provided parameters or
     * null if the DiscoveryManagement instance was not created by the
     * DiscoveryManagementPool
     *
     * @throws IOException If discovery management cannot be created
     */
    public LookupCache getLookupCache(final DiscoveryManagement dMgr, final ServiceTemplate template) throws IOException {
        if(!(dMgr instanceof DiscoveryManagementPool.SharedDiscoveryManager)) {
            logger.warn("The DiscoveryManagement instance passed was not created by the {}, returning null",
                        DiscoveryManagementPool.class.getName());
            return(null);
        }
        DiscoveryManagementPool.SharedDiscoveryManager sharedDM = (DiscoveryManagementPool.SharedDiscoveryManager)dMgr;
        return(getLookupCache(sharedDM.getSharedName(), sharedDM.getGroups(), sharedDM.getLocators(), template));
    }
   
    /**
     * This method will return an instance of LookupCache based on matching the
     * shared name, shared discovery name, groups, locators and ServiceTemplate
     * provided as criteria. If there is an existing LookupCache instance created
     * by a ServiceDiscoveryManager instance this utility has created that matches
     * the supplied criteria, that instance will be returned.
     *
     * <p>If a LookupCache can not be found due to not being able to match discovery
     * criteria, a new ServiceDiscoveryManager instance will be created using the
     * provided discovery criteria, and a LookupCache instance created and returned.
     *
     * <p>If a ServiceDiscoveryManager can be matched, but not a LookupCache, a new
     * LookupCache will be created using the matched ServiceDiscoveryManager
     *
     * @param sharedName The name the LookupCache instances are shared across
     * @param groups An array of String objects indicating the Jini Lookup
     * Service groups to discover
     * @param locators An array of LookupLocator objects indicating specific
     * Jini Lookup Service instances to discover
     * @param template ServiceTemplate to match
     * @return A ServiceDiscoveryManager object based on the provided parameters
     *
     * @throws IOException If discovery management cannot be created
     */
    public LookupCache getLookupCache(final String sharedName,
                                      final String[] groups,
                                      final LookupLocator[] locators,
                                      final ServiceTemplate template) throws IOException {
        if(template==null)
            throw new IllegalArgumentException("template is null");
        SDMWrapper sdmWrapper;
        try {
            sdmWrapper = getSDMWrapper(sharedName, groups, locators);
        } catch(ConfigurationException e) {
            throw new IOException("Configuration problem creating a SDMWrapper", e);
        }
        return(sdmWrapper.getLookupCache(template, true));
    }
   
    /**
     * For all ServiceDiscoveryManager instances this utility has created, terminate
     * them and set the singleton instance to null;
     */
    public void terminate() {
        SDMWrapper[] sdms = getSDMWrappers();
        for(SDMWrapper sdmWrapper : sdms) {
            sdmWrapper.sdm.terminate();
        }
        pool.clear();
        singleton = null;       
    }

    private SDMWrapper[] getSDMWrappers() {
        SDMWrapper[] sdms;
        synchronized(pool) {
            sdms = pool.toArray(new SDMWrapper[pool.size()]);
        }
        return sdms;
    }

    /*
     * Get an SDMWrapper which matches the sharedName, discovery criteria, or
     * create one if not found
     */
    private SDMWrapper getSDMWrapper(final String sharedName,
                                     final String[] groupsToMatch,
                                     final LookupLocator[] locatorsToMatch) throws IOException, ConfigurationException {
        SDMWrapper sdmWrapper = null;
        SDMWrapper[] sdms = getSDMWrappers();
        for(SDMWrapper wrapper : sdms) {
            if(wrapper.namesMatch(sharedName) &&
               wrapper.groupsMatch(groupsToMatch) &&
               wrapper.locatorsMatch(locatorsToMatch)) {
                sdmWrapper = wrapper;
                break;
            }
        }

        if(sdmWrapper==null) {           
            config = (config==null?EmptyConfiguration.INSTANCE:config);
            ServiceDiscoveryManager sdm =
                new ServiceDiscoveryManager(DiscoveryManagementPool.getInstance().getDiscoveryManager(sharedName,
                                                                                                      groupsToMatch,
                                                                                                      locatorsToMatch),
                                            new LeaseRenewalManager(config),
                                            config);
                   
            sdmWrapper = new SDMWrapper(sharedName, sdm, groupsToMatch, locatorsToMatch);
            synchronized(pool) {
                pool.add(sdmWrapper);
            }
        }
        return(sdmWrapper);
    }
   
    /**
     * The SDMWrapper class provides a wrapper around a known ServiceDiscoveryManager,
     * the lookupCache instances that this utility has created using the
     * ServiceDiscoveryManager and checks to see if the referenced
     * ServiceDiscoveryManager matches specified criteria, or if any known
     * LookupCache instances match criteria
     */
    class SDMWrapper {
        final String sharedName;
        final ServiceDiscoveryManager sdm;
        String[] groups;
        final LookupLocator[] locators;
        final Hashtable<ServiceTemplate, SharedLookupCache> cacheTable = new Hashtable<ServiceTemplate, SharedLookupCache>();
       
        SDMWrapper(final String sharedName, final ServiceDiscoveryManager sdm, final String[] groups, final LookupLocator[] locators) {
            this.sharedName = sharedName;
            this.sdm = sdm;
            if(groups!=null) {
                this.groups = new String[groups.length];
                System.arraycopy(groups, 0, this.groups, 0, this.groups.length);
            }
            this.locators = new LookupLocator[locators.length];
            System.arraycopy(locators, 0, this.locators, 0, this.locators.length);
        }
       
        void removeCache(final SharedLookupCache lCache) {
            ServiceTemplate templateToMatch = lCache.getServiceTemplate();
            for(Enumeration<ServiceTemplate> en=cacheTable.keys(); en.hasMoreElements();) {
                ServiceTemplate template = en.nextElement();
                if(templatesMatch(template, templateToMatch)) {
                    cacheTable.remove(templateToMatch);
                    break;
                }
            }
            logger.trace("removeCache(), cacheTable.size()=={}",cacheTable.size());
            if(cacheTable.size()==0) {
                try {
                    sdm.terminate();
                } catch (IllegalStateException e) {
                    logger.trace("Terminating SDM", e);
                }
                synchronized(pool) {
                    pool.remove(this);
                }
            }
        }
       
        /*
         * See if the sharedNames match
         *
         * @param name
         *
         * @return true if they do, false otherwise
         */
        boolean namesMatch(final String name) {
            if(sharedName==null && name==null)
                return(true);
            if(sharedName==null)
                return(false);
            if(name == null)
                return (false);
            return(sharedName.equals(name));
        }              
               
        /**
         * See if the groups provided match the groups for the referenced SDM
         *
         * @param groupsToMatch The groups
         *
         * @return true if they do, false otherwise
         */
        boolean groupsMatch(final String[] groupsToMatch) {
            /* If both are set to ALL_GROUPS we have a match */
            if(groupsToMatch == LookupDiscovery.ALL_GROUPS &&
               groups == LookupDiscovery.ALL_GROUPS) {
                return(true);
            }
            /* If one or the other is set to ALL_GROUPS return false */
            if(groupsToMatch == LookupDiscovery.ALL_GROUPS ||
               groups == LookupDiscovery.ALL_GROUPS)
                return(false);
           
            /* If both have the same "set", check for equivalence */
            if(groupsToMatch.length == groups.length) {
                int matches=0;
                for(int i=0; i<groupsToMatch.length; i++) {
                    if(groupsToMatch[i].equals(groups[i]))
                        matches++;
                }
                if(matches==groupsToMatch.length) {
                    return(true);
                }
            }
            return(false);
        }       
       
        /**
         * See if the locators provided match the locators for the referenced SDM
         *
         * @param locatorsToMatch  The locators
         *
         * @return true if they do. false otherwise
         */
        boolean locatorsMatch(final LookupLocator[] locatorsToMatch) {
            boolean matched=false;
            if(locatorsToMatch == null && locators == null)
                return(true);
            if(locatorsToMatch == null && locators.length==0)
                return(true);
            if(locatorsToMatch!=null && locators!=null) {
                if(locatorsToMatch.length == locators.length) {
                    int matches=0;
                    for(int i=0; i<locatorsToMatch.length; i++) {
                        if(locatorsToMatch[i].equals(locators[i]))
                            matches++;
                    }
                    if(matches==locatorsToMatch.length)
                        matched=true;
                }
            }
            return(matched);
        }
               
        /**
         * Get a LookupCache from the cacheTable for the provided ServiceTemplate.
         *
         * @param templateToMatch The template
         * @param create If true and a a LookupCache does not exist, create one
         *
         * @return A SharedLookupCache for the ServiceTemplate
         *
         * @throws IOException If a LookupCache cannot be created
         */
        SharedLookupCache getLookupCache(final ServiceTemplate templateToMatch, final boolean create) throws IOException {
            SharedLookupCache lCache = null;
            for(Enumeration en=cacheTable.keys(); en.hasMoreElements();) {
                ServiceTemplate template = (ServiceTemplate)en.nextElement();
                if(templatesMatch(template, templateToMatch)) {
                    lCache = cacheTable.get(template);
                    break;
                }
            }
            if(lCache==null && create) {
                ServiceItemFilter filter = (sharedName==null?null: new OpStringFilter(sharedName));
                LookupCache lc = sdm.createLookupCache(templateToMatch, filter, null);
                lCache = new SharedLookupCache(lc, templateToMatch, this);
                lCache.setServiceItemFilter(filter);
                cacheTable.put(templateToMatch, lCache);
            }           
            return(lCache);
        }
       
        /**
         * Determine of the ServiceTemplate instances match each other
         *
         * @param st1 ServiceTemplate instance 1
         * @param st2 ServiceTemplate instance 2
         *
         * @return true if they match
         */
        boolean templatesMatch(final ServiceTemplate st1, final ServiceTemplate st2) {
            if(attributesMatch(st1.attributeSetTemplates, st2.attributeSetTemplates) &&
               serviceIDsMatch(st1.serviceID, st2.serviceID) &&
               serviceTypesMatch(st1.serviceTypes, st2.serviceTypes)) {
                return(true);
            }                                     
            return(false);
        }
    }
       
    /*
     * Check if attributes match
     */
    boolean attributesMatch(final Entry[] attr1, final Entry[] attr2) {
        if(attr1==null && attr2==null)
            return(true);
        if(attr1==null || attr2==null)
            return(false);
        return(LookupAttributes.equal(attr1, attr2));
    }
   
    /*
     * Check if service ID match
     */
    boolean serviceIDsMatch(final ServiceID sid1, final ServiceID sid2) {
        if(sid1==null && sid2==null)
            return(true);
        if(sid1==null || sid2==null)
            return(false);
        return(sid1.equals(sid2));
    }
   
    /*
     * Check if service types match
     */
    boolean serviceTypesMatch(final Class[] types1, final Class[] types2) {
        if(types1==null && types2==null)
            return(true);
        if(types1==null || types2==null)
            return(false);
        if(types1.length == types2.length) {           
            int matches = 0;
            for (Class c1 : types1) {
                for (Class c2 : types2) {
                    if (c1.getName().equals(c2.getName()))
                        matches++;
                }
            }
            if(matches==types1.length)
                return(true);
        }
        return(false);
    }   
   
    /**
     * The SharedLookupCache implements a LookupCache and delegates all method
     * invocations to it's LookupDiscoveryManager, and maintains a
     * reference counter for how many clients are sharing the instance. The reference
     * counter is used to determine if the LookupDiscoveryManager should be
     * terminated. The reference counter is increments each time this instance is
     * shared, and decremented each time the terminate method is called. If the
     * reference counter goes to zero upon termination the LookupDiscoveryManager
     * will be terminated
     */
    public class SharedLookupCache implements LookupCache {
        private final LookupCache lCache;
        private final ServiceTemplate template;
        private final AtomicInteger refCounter = new AtomicInteger();
        private final SDMWrapper sdmWrapper;
        private boolean terminated = false;
        private ServiceItemFilter filter;
        private final List<ServiceDiscoveryListener> localListeners = new ArrayList<ServiceDiscoveryListener>();
       
        public SharedLookupCache(final LookupCache lCache,
                                 final ServiceTemplate template,
                                 final SDMWrapper sdmWrapper) {
            this.lCache = lCache;
            this.template = template;
            this.sdmWrapper = sdmWrapper;
        }               

        void setServiceItemFilter(final ServiceItemFilter filter) {
            this.filter = filter;
        }

        /*
         * Get the ServiceTemplate
         */
        private ServiceTemplate getServiceTemplate() {
            return(template);
        }

        /* (non-Javadoc)
         * @see net.jini.lookup.LookupCache#lookup(ServiceItemFilter)
         */
        public ServiceItem lookup(final ServiceItemFilter filter) {
            return(lCache.lookup(filter));
        }

        /* (non-Javadoc)
         * @see LookupCache#lookup(ServiceItemFilter, int)
         */
        public ServiceItem[] lookup(final ServiceItemFilter filter, final int maxMatches) {
            return(lCache.lookup(filter, maxMatches));
        }

        /* (non-Javadoc)
         * @see ServiceDiscoveryManager#lookup(ServiceTemplate, int, ServiceItemFilter)
         */
        public ServiceItem[] lookupRemote(final ServiceItemFilter filter, final int maxMatches) {
            return sdmWrapper.sdm.lookup(template, maxMatches, filter);
        }

        /* (non-Javadoc)
         * @see LookupCache#addListener(ServiceDiscoveryListener)
         */
        public synchronized void addListener(final ServiceDiscoveryListener listener) {
            refCounter.incrementAndGet();
            logger.trace("Added LookupCache Listener for template [{}], refCounter: {}",
                         getServiceTemplateAsString(), refCounter.get());
            synchronized(localListeners) {
                localListeners.add(listener);
            }
            lCache.addListener(listener);
        }

        /* (non-Javadoc)
         * @see LookupCache#removeListener(ServiceDiscoveryListener)
         */
        public synchronized void removeListener(final ServiceDiscoveryListener listener) {
            if(!terminated) {
                synchronized(localListeners) {
                    localListeners.remove(listener);
                }
                lCache.removeListener(listener);
                refCounter.decrementAndGet();
            }
            logger.trace("Removed LookupCache Listener for template [{}], refCounter: {}",
                         getServiceTemplateAsString(), refCounter.get());
            logger.trace("lCache={} refCounter={}", lCache.toString(), refCounter.get());
            if(refCounter.get()==0) {
                terminate();
            }
        }

        /* (non-Javadoc)
         * @see LookupCache#discard(Object)
         */
        public void discard(final Object o) {
            logger.trace("Discard {} from LookupCache", o.getClass().getName());
            try {
                lCache.discard(o);
            } catch(IllegalStateException e) {
                logger.warn("Could not discard {}, {}", o.getClass().getName(), e.getMessage());
            }

        }

        /* (non-Javadoc)
         * @see net.jini.lookup.LookupCache#terminate()
         */
        public synchronized void terminate() {
            if(refCounter.get()==0) {
                logger.trace("Terminating LookupCache for template [{}], refCounter: {}",
                             getServiceTemplateAsString(), refCounter.get());
                terminated = true;
                lCache.terminate();
                sdmWrapper.removeCache(this);
            }
        }

        String getServiceTemplateAsString() {
            StringBuilder sb1 = new StringBuilder();
            StringBuilder sb2 = new StringBuilder();
            if(template.serviceTypes!=null) {
                for(Class c : template.serviceTypes) {
                    if(sb2.length()>0)
                        sb2.append(", ");
                    sb2.append(c.getName());
                }
            }
            sb1.append("types: [").append(sb2.toString()).append("] ");
            sb2.delete(0, sb2.length());
            if(template.attributeSetTemplates!=null) {
                for(Entry e : template.attributeSetTemplates) {
                    if(sb2.length()>0)
                        sb2.append(", ");
                    if(e instanceof Name)
                        sb2.append("Name: ").append(((Name)e).name);
                    else
                        sb2.append(e.getClass().getName());
                }
            }
            sb1.append("attributes: [").append(sb2.toString()).append("] ");
            return sb1.toString();

        }

        void notifyOnLocalAdd(final ServiceItem item) {
            boolean cleared = true;
            if(filter!=null) {
                cleared = filter.check(item);
            }
            if(cleared) {
                ServiceDiscoveryListener[] listeners;
                synchronized(localListeners) {
                    listeners = localListeners.toArray(new ServiceDiscoveryListener[localListeners.size()]);
                }
                for(ServiceDiscoveryListener l : listeners) {
                    l.serviceAdded(new ServiceDiscoveryEvent(this, null, item));
                }
            }
        }
    }

    class ContainerListener implements ServiceBeanContainerListener {
        private final ServiceBeanContainer container;
        private final List<ServiceRecord> notified = new ArrayList<ServiceRecord>();

        ContainerListener(final ServiceBeanContainer container) {
            this.container = container;
            container.addListener(this);
        }

        private ServiceBeanInstance getServiceBeanInstance(final ServiceRecord r) {
            ServiceBeanDelegate delegate = container.getServiceBeanDelegate(r.getServiceID());
            return delegate.getServiceBeanInstance();
        }

        List<Class<?>> getAllInterfaces(Class<?> classObject) {
            List<Class<?>> list = new ArrayList<Class<?>>();
            if(classObject.getName().equals(Object.class.getName()))
                return list;
            list.addAll(getAllInterfaces(classObject.getSuperclass()));
            Collections.addAll(list, classObject.getInterfaces());
            return list;
        }

        public void serviceInstantiated(final ServiceRecord record) {
            try {
                ServiceBeanInstance instance = getServiceBeanInstance(record);
                Class<?> proxyClass = instance.getService().getClass();
                Class<?> theInterfaceClass = null;
                for(Class<?> interfaceClass : getAllInterfaces(proxyClass)) {
                    for(ClassBundle cb : record.getServiceElement().getExportBundles()) {
                        if(interfaceClass.getName().equals(cb.getClassName())) {
                            theInterfaceClass = interfaceClass;
                            break;
                        }
                    }
                    if(theInterfaceClass!=null)
                        break;
                }
                if(theInterfaceClass==null) {
                    logger.warn("No matching interface class found for {}, defaulting to {}",
                                record.getServiceElement().getName(), Service.class.getName());
                    theInterfaceClass = Service.class;
                }
                logger.trace("[{}] selected: {}", record.getServiceElement().getName(), theInterfaceClass.getName());
                ServiceTemplate templateToMatch = JiniClient.getServiceTemplate(record.getServiceElement(),
                                                                                theInterfaceClass);
                SDMWrapper[] sdms = getSDMWrappers();
                for(SDMWrapper sdm : sdms) {
                    SharedLookupCache lCache = sdm.getLookupCache(templateToMatch, false);
                    if(lCache!=null) {
                        boolean alreadyNotified;
                        synchronized(notified) {
                            alreadyNotified = notified.contains(record);
                        }
                        if(!alreadyNotified) {
                            logger.trace("Notify listeners of local instantiation of {} {}",
                                         record.getServiceElement().getName(), theInterfaceClass.getName());
                            lCache.notifyOnLocalAdd(makeServiceItem(instance));
                            synchronized(notified) {
                                notified.add(record);
                            }
                        }
                    }
                }
            } catch (Exception e) {
                logger.warn("Unable to load service interface", e);
            }
        }

        public void serviceDiscarded(final ServiceRecord record) {
            synchronized(notified) {
                notified.remove(record);
            }
        }

        private ServiceItem makeServiceItem(final ServiceBeanInstance instance) throws IOException,
                                                                                       ClassNotFoundException {
            Uuid uuid = instance.getServiceBeanID();
            ServiceID serviceID = new ServiceID(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits());
            Entry[] attrs = null;
            Object service = instance.getService();
            if(service instanceof Administrable) {
                try {
                    Object admin = ((Administrable)service).getAdmin();
                    if(admin instanceof JoinAdmin) {
                        attrs = ((JoinAdmin)admin).getLookupAttributes();
                    }
                } catch(RemoteException e) {
                    logger.warn("Getting attributes from [{}]", instance.getServiceBeanConfig().getName(), e);
                }
            }
            return(new ServiceItem(serviceID, service, attrs));
        }
    }
}
TOP

Related Classes of org.rioproject.impl.client.LookupCachePool$ContainerListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.