/* * Copyright 2008 the original author or authors. * Copyright 2005 Sun Microsystems, Inc. * * 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 net.jini.core.discovery.LookupLocator; import net.jini.core.entry.Entry; import net.jini.core.lookup.ServiceRegistrar; import net.jini.core.lookup.ServiceTemplate; import net.jini.discovery.*; import net.jini.lookup.entry.Name; import org.rioproject.associations.AssociationDescriptor; import org.rioproject.servicebean.ServiceBeanContext; import org.rioproject.opstring.ServiceElement; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.MalformedURLException; import java.rmi.RemoteException; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.*; /** * The JiniClient class is a helper class that Jini clients (or something that * wants to act like a Jini client) uses to create DiscoveryManagement instances * to discover services * * @author Dennis Reedy */ public class JiniClient { public static final String GROUPS_PROPERTY_NAME = org.rioproject.config.Constants.GROUPS_PROPERTY_NAME; public static final String LOCATOR_PROPERTY_NAME = org.rioproject.config.Constants.LOCATOR_PROPERTY_NAME; private DiscoveryManagement discoverer = null; public Listener listener = null; private List<ServiceRegistrar> regArray; private boolean createdDiscoverer = false; private static Logger logger = LoggerFactory.getLogger(JiniClient.class); /** * Create an instance of a JiniClient <br> * * @throws Exception if any errors occur */ public JiniClient() throws Exception { this(null); } /** * Create an instance of a JiniClient providing a DiscoveryManagement * reference <br> * * @param dm The DiscoveryManagement instance to use. If the * DiscoveryManagement object is null, JiniClient will look for 2 * system properties to construct a DiscoveryManagement object: * <ul> * <li>org.rioproject.groups: a comma separated list of groups to use. If this * property is not found LookupDiscoveryGroups will be set to * LookupDiscovery.NO_GROUPS. Additionally, the value of "all" will be set * to LookupDiscovery.ALL_GROUPS * <li>org.rioproject.locators: a comma separated list of * LookupLocator formatted URLs * </ul> * * @throws Exception if any errors occur */ public JiniClient(DiscoveryManagement dm) throws Exception { regArray = Collections.synchronizedList(new ArrayList<ServiceRegistrar>()); listener = new Listener(); if(dm != null) { discoverer = dm; dm.addDiscoveryListener(listener); } else { String[] groups = parseGroups(System.getProperty(GROUPS_PROPERTY_NAME)); LookupLocator[] locators = parseLocators(System.getProperty( LOCATOR_PROPERTY_NAME)); if(logger.isDebugEnabled()) logger.debug("Starting discovery process..."); discoverer = new LookupDiscoveryManager(groups, locators, listener); createdDiscoverer = true; } } /** * Parse a comma or space delimited string of group names * * @param groupNames The string of group names to parse * * @return A string array of parsed group names as follows: * <ul> * <li>If the group names parameter is null, return * {@link net.jini.discovery.LookupDiscovery#NO_GROUPS}. * <li>If the groupNames value is "all", return * {@link net.jini.discovery.LookupDiscovery#ALL_GROUPS}. * <li> If one of the groupNames value is "public", replace * "public" with an empty string "". * <li>Otherwise add the groupName value as a element in the string array */ public static String[] parseGroups(String groupNames) { String[] groups; if(groupNames == null) { groups = LookupDiscovery.NO_GROUPS; if(logger.isDebugEnabled()) logger.debug("Set groups to NO_GROUPS"); } else { if(logger.isDebugEnabled()) logger.debug("Set groups to [" + groupNames + "]"); StringTokenizer st = new StringTokenizer(groupNames, " \t\n\r\f,"); groups = new String[st.countTokens()]; if(groups.length == 1) { groups[0] = st.nextToken(); if(groups[0].equals("all")) { groups = LookupDiscovery.ALL_GROUPS; } else { if(groups[0].equals("public")) groups[0] = ""; } } else { for(int i = 0; st.hasMoreTokens(); i++) { String g = st.nextToken(); if(g.equals("public")) g = ""; groups[i] = g; } } } return groups; } /** * Utility to return a formatted string of discovery attributes */ public static String getDiscoveryAttributes(ServiceBeanContext context) { String[] g = context.getServiceBeanConfig().getGroups(); StringBuilder buff = new StringBuilder(); if(g!= LookupDiscovery.ALL_GROUPS) { for(int i=0; i<g.length; i++) { if(i>0) buff.append(", "); buff.append(g[i]); } } else { buff.append("ALL_GROUPS"); } if(context.getServiceBeanConfig().getLocators()!=null) { for(LookupLocator locator : context.getServiceBeanConfig().getLocators()) { if(buff.length()>0) buff.append(", "); buff.append(locator.toString()); } } return buff.toString(); } /** * Parse a comma or space delimited string of locator urls * * @param locatorUrls The string of locator urls to parse * * @return A {@link net.jini.core.discovery.LookupLocator} array of * parsed locator urls. If the locatorUrls parameter is null, return null * * @throws MalformedURLException If the locatorUrls contains a value that has an illegal format */ public static LookupLocator[] parseLocators(String locatorUrls) throws MalformedURLException { LookupLocator[] locators = null; if(locatorUrls != null) { if(logger.isDebugEnabled()) logger.debug("Use unicast discovery"); StringTokenizer st = new StringTokenizer(locatorUrls, " \t\n\r\f,"); List<LookupLocator> list = new LinkedList<LookupLocator>(); while (st.hasMoreTokens()) { String locator = st.nextToken(); if(!locator.startsWith("jini://")) locator = "jini://"+locator; list.add(new LookupLocator(locator)); if(logger.isDebugEnabled()) logger.debug("Add locator : " + locator); } locators = list.toArray(new LookupLocator[list.size()]); } return locators; } /** * Create a ServiceTemplate from a ServiceElement * * @param sElem A ServiceElement * @param interfaceClass The interface class use * * @return A ServiceTemplate which can be used to discover the service * * @throws IllegalArgumentException if the {@code sElem} or {@code interfaceClass} arguments are {@code null} */ public static ServiceTemplate getServiceTemplate(final ServiceElement sElem, final Class<?> interfaceClass) { if(sElem == null) throw new IllegalArgumentException("sElem is null"); if(interfaceClass == null) throw new IllegalArgumentException("interfaceClass is null"); ServiceTemplate template; if(sElem.getMatchOnName()) template = new ServiceTemplate(null, new Class[]{interfaceClass}, new Entry[]{new Name(sElem.getName())}); else template = new ServiceTemplate(null, new Class[]{interfaceClass}, null); return (template); } /** * Get a DiscoveryManagement instance from service attributes in a * ServiceElement * * @param sElem A ServiceElement * @return A DiscoveryManagement instance based on * service discovery attributes * * @throws IOException If DiscoveryManagement cannot be created */ public static DiscoveryManagement getDiscoveryManagement(ServiceElement sElem) throws IOException { if(sElem == null) throw new IllegalArgumentException("sElem is null"); DiscoveryManagementPool discoPool = DiscoveryManagementPool.getInstance(); return (discoPool.getDiscoveryManager(sElem.getOperationalStringName(), sElem.getServiceBeanConfig().getGroups(), sElem.getServiceBeanConfig().getLocators())); } /** * Create a ServiceTemplate from an AssociationDescriptor * * @param aDesc The AssociationDescriptor * @param cl The ClassLoader to use to load the interface class. If null, * the threads context classloader will be used * * @return A ServiceTemplate which can be used to discover the service * * @throws ClassNotFoundException If the interface class cannot be loaded */ public static ServiceTemplate getServiceTemplate(AssociationDescriptor aDesc, ClassLoader cl) throws ClassNotFoundException { if(aDesc == null) throw new IllegalArgumentException("aDesc is null"); ServiceTemplate template ; String[] iNames = aDesc.getInterfaceNames(); Class[] interfaces = new Class[iNames.length]; ClassLoader loader = cl; if(loader==null) { final Thread currentThread = Thread.currentThread(); loader = AccessController.doPrivileged( new PrivilegedAction<ClassLoader>() { public ClassLoader run() { return (currentThread.getContextClassLoader()); } }); } for(int i = 0; i < interfaces.length; i++) { interfaces[i] = Class.forName(iNames[i], false, loader); } if(aDesc.matchOnName()) template = new ServiceTemplate(null, interfaces, new Entry[]{new Name(aDesc.getName())}); else template = new ServiceTemplate(null, interfaces, null); return (template); } /** * Get the DiscoveryManagement instance * * @return Returns the instance of DiscoveryManagement that was either * passed into the constructor, or that was created as a result of null * being input to that parameter. */ public DiscoveryManagement getDiscoveryManager() { return discoverer; } /** * Add a Locator to discover. The method will construct a new LookupLocator * object, set to perform unicast discovery to the given host and port * <p> * If a <code>DiscoveryManagement</code> is provided to JiniClient and * does not implement the DiscoveryLocatorManagement interface, no action is * taken * * @param host The hostname part of the locator * @param port The port name part of the locator */ public void addLocator(String host, int port) { if(discoverer instanceof DiscoveryLocatorManagement) ((DiscoveryLocatorManagement)discoverer).addLocators(new LookupLocator[]{new LookupLocator(host, port)}); } /** * Get the array of known locators * * @return Returns an array consisting of the elements of the managed set of * locators; that is, instances of LookupLocator in which each instance * corresponds to a specific lookup service to discover. The returned set * will include both the set of LookupLocators corresponding to lookup * services that have already been discovered as well as the set of those * that have not yet been discovered. If the managed set of locators is * empty, this method will return the empty array. This method returns a new * array upon each invocation. * <p> * If a <code>DiscoveryManagement</code> is provided to JiniClient and * does not implement the DiscoveryLocatorManagement interface, this method * will return null */ public LookupLocator[] getLocators() { if(discoverer instanceof DiscoveryLocatorManagement) { return (((DiscoveryLocatorManagement)discoverer).getLocators()); } return (new LookupLocator[0]); } /** * Deletes a set of locators from the managed set of locators, and discards * any already-discovered lookup service that corresponds to a deleted * locator. * <p> * If a <code>DiscoveryManagement</code> is provided to JiniClient and * does not implement the DiscoveryLocatorManagement interface, no action is * taken * * @param locators Array of LookupLocator instances to remove */ public void removeLocators(LookupLocator[] locators) { if(discoverer instanceof DiscoveryLocatorManagement) { ((DiscoveryLocatorManagement)discoverer).removeLocators(locators); } } /** * Add a list of groups to be discovered * <p> * If a <code>DiscoveryManagement</code> is provided to JiniClient and * does not implement the DiscoveryGroupManagement interface, no action is * taken * * @param gAdd Array of group names to add * * @throws IOException if the groups could not be added */ public void addRegistrarGroups(String[] gAdd) throws IOException { if(discoverer instanceof DiscoveryGroupManagement) ((DiscoveryGroupManagement)discoverer).addGroups(gAdd); } /** * Get the known set of groups * * @return Returns an array consisting of the elements of the managed set * of groups; that is, the names of the groups whose members are the lookup * services to discover. If the managed set of groups is empty, this method * will return the empty array. If there is no managed set of groups, or * network then null is returned; indicating that all groups are to be * discovered. If for some reason network errors occur, null is returned. */ public String[] getRegistrarGroups() { try { return (((DiscoveryGroupManagement)discoverer).getGroups()); } catch(Exception e) { logger.error("Getting Registrar Groups", e); } return (new String[0]); } /** * Remove a list of groups from discovery management * * @param gRemove The array of groups to remove */ public void removeRegistrarGroups(String[] gRemove) { if(discoverer instanceof DiscoveryGroupManagement) ((DiscoveryGroupManagement)discoverer).removeGroups(gRemove); } /** * Stop this JiniClient and terminate discovery management. This method will * also terminate all ServiceCache instances that it has created */ public void terminate() { if(createdDiscoverer) stopDiscoverer(); regArray.clear(); } void stopDiscoverer() { if(listener != null) { if(discoverer != null) discoverer.removeDiscoveryListener(listener); listener = null; } if(discoverer != null) { discoverer.terminate(); discoverer = null; } } public class Listener implements DiscoveryListener { public void discovered(DiscoveryEvent de) { try { ServiceRegistrar[] registrars = de.getRegistrars(); for (ServiceRegistrar registrar : registrars) { if (logger.isDebugEnabled() && createdDiscoverer) { LookupLocator lookup = registrar.getLocator(); String host = lookup.getHost(); logger.debug("Discovered JLS on host [" + host + "]"); } regArray.add(registrar); } synchronized(this) { notifyAll(); } } catch(RemoteException e) { logger.error("Discovered ServiceRegistrar", e); } } public void discarded(DiscoveryEvent dEvent) { ServiceRegistrar[] registrars = dEvent.getRegistrars(); for (ServiceRegistrar registrar : registrars) { for (ServiceRegistrar r : regArray) { if (registrar.equals(r)) { if (logger.isDebugEnabled()) { try { LookupLocator lookup = registrar.getLocator(); String host = lookup.getHost(); logger.debug("Discarded JLS on host [" + host + "]"); } catch (RemoteException e) { logger.error("Getting LookupLocator during discarded notification", e); } } regArray.remove(regArray.indexOf(r)); break; } } } } } }