/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.remoting;
import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.jboss.logging.Logger;
import org.jboss.remoting.serialization.ClassLoaderUtility;
import org.jboss.remoting.transport.ClientInvoker;
import org.jboss.remoting.transport.local.LocalClientInvoker;
/**
* InvokerRegistry is a simple registery for creating client and server side Invoker implementations,
* getting information about the invokers and register as a invoker creator for one or more specific
* transports.
*
* @author <a href="mailto:jhaynie@vocalocity.net">Jeff Haynie</a>
* @author <a href="mailto:telrod@e2technologies.net">Tom Elrod</a>
* @version $Revision: 1.22.2.1 $
*/
public class InvokerRegistry
{
private static final Logger log = Logger.getLogger(InvokerRegistry.class);
// holds the invoker classes (alrady loaded)
private static final Map clientInvokers = new HashMap();
private static final Map serverInvokers = new HashMap();
private static final Map clientLocators = new HashMap();
private static final Map serverLocators = new HashMap();
private static final Set registeredLocators = new HashSet();
private static final Object serverLock = new Object();
private static final Object clientLock = new Object();
private static final Map clientInvokerFQN = new HashMap();
private static final Map serverInvokerFQN = new HashMap();
static
{
// auto-register class name (as Strings) of some standard invokers. Doing String form so can be loaded lazily.
clientInvokerFQN.put("socket", "org.jboss.remoting.transport.socket.SocketClientInvoker");
serverInvokerFQN.put("socket", "org.jboss.remoting.transport.socket.SocketServerInvoker");
clientInvokerFQN.put("sslsocket", "org.jboss.remoting.transport.socket.ssl.SSLSocketClientInvoker");
serverInvokerFQN.put("sslsocket", "org.jboss.remoting.transport.socket.ssl.SSLSocketServerInvoker");
clientInvokerFQN.put("rmi", "org.jboss.remoting.transport.rmi.RMIClientInvoker");
serverInvokerFQN.put("rmi", "org.jboss.remoting.transport.rmi.RMIServerInvoker");
clientInvokerFQN.put("http", "org.jboss.remoting.transport.http.HTTPClientInvoker");
serverInvokerFQN.put("http", "org.jboss.remoting.transport.coyote.CoyoteInvoker");
clientInvokerFQN.put("https", "org.jboss.remoting.transport.http.ssl.HTTPSClientInvoker");
serverInvokerFQN.put("https", "org.jboss.remoting.transport.coyote.CoyoteInvoker");
/*** The legacy http invoker implementation ***
clientInvokerFQN.put("http", "org.jboss.remoting.transport.http.HTTPClientInvoker");
serverInvokerFQN.put("http", "org.jboss.remoting.transport.http.HTTPServerInvoker");
clientInvokerFQN.put("https", "org.jboss.remoting.transport.http.ssl.HTTPSClientInvoker");
serverInvokerFQN.put("https", "org.jboss.remoting.transport.http.ssl.HTTPSServerInvoker");
*/
clientInvokerFQN.put("servlet", "org.jboss.remoting.transport.http.ssl.HTTPSClientInvoker");
serverInvokerFQN.put("servlet", "org.jboss.remoting.transport.servlet.ServletServerInvoker");
clientInvokerFQN.put("multiplex", "org.jboss.remoting.transport.multiplex.MultiplexClientInvoker");
serverInvokerFQN.put("multiplex", "org.jboss.remoting.transport.multiplex.MultiplexServerInvoker");
clientInvokerFQN.put("sslmultiplex", "org.jboss.remoting.transport.multiplex.ssl.SSLMultiplexClientInvoker");
serverInvokerFQN.put("sslmultiplex", "org.jboss.remoting.transport.multiplex.ssl.SSLMultiplexServerInvoker");
}
/**
* return an array of InvokerLocators that are local to this VM (server invokers)
*
* @return
*/
public static synchronized final InvokerLocator[] getRegisteredServerLocators()
{
return (InvokerLocator[]) registeredLocators.toArray(new InvokerLocator[registeredLocators.size()]);
}
/**
* return a suitable local server invoker that can service the remote invoker locator based on
* a compatible transport
*
* @param remote
* @return
*/
public static synchronized InvokerLocator getSuitableServerLocatorForRemote(InvokerLocator remote)
{
Iterator iter = registeredLocators.iterator();
while(iter.hasNext())
{
InvokerLocator l = (InvokerLocator) iter.next();
if(l.getProtocol().equals(remote.getProtocol()))
{
// we found a valid transport match
return l;
}
}
return null;
}
/**
* return an array of String of the registered transports
*
* @return
*/
public static final String[] getRegisteredInvokerTransports()
{
synchronized(clientLock)
{
Set set = clientInvokers.keySet();
String transports[] = new String[set.size()];
return (String[]) set.toArray(transports);
}
}
/**
* return an array of ClientInvokers that are connected
*
* @return
*/
public static final ClientInvoker[] getClientInvokers()
{
synchronized(clientLock)
{
if(clientLocators.isEmpty())
{
return new ClientInvoker[0];
}
Collection collection = clientLocators.values();
return (ClientInvoker[]) collection.toArray(new ClientInvoker[collection.size()]);
}
}
/**
* return an array of ServerInvokers that are connected
*
* @return
*/
public static final ServerInvoker[] getServerInvokers()
{
synchronized(serverLock)
{
if(serverLocators.isEmpty())
{
return new ServerInvoker[0];
}
Collection collection = serverLocators.values();
return (ServerInvoker[]) collection.toArray(new ServerInvoker[collection.size()]);
}
}
/**
* register a client/server invoker Class pair for a given transport
*
* @param transport
* @param clientClassName
* @param serverClassName
*/
public static synchronized void registerInvoker(String transport, String clientClassName, String serverClassName)
{
clientInvokerFQN.put(transport, clientClassName);
serverInvokerFQN.put(transport, serverClassName);
}
/**
* register a client/server invoker Class pair for a given transport
*
* @param transport
* @param client
* @param server
*/
public static synchronized void registerInvoker(String transport, Class client, Class server)
{
clientInvokers.put(transport, client);
serverInvokers.put(transport, server);
}
/**
* unregister a client/server invoker pair for the given transport
*
* @param transport
*/
public static synchronized void unregisterInvoker(String transport)
{
clientInvokerFQN.remove(transport);
clientInvokers.remove(transport);
serverInvokerFQN.remove(transport);
serverInvokers.remove(transport);
}
public static synchronized void unregisterLocator(InvokerLocator locator)
{
serverLocators.remove(locator);
registeredLocators.remove(locator);
}
/**
* returns true if the client invoker is registered in the local JVM for a given locator
*
* @param locator
* @return
*/
public static boolean isClientInvokerRegistered(InvokerLocator locator)
{
synchronized(clientLock)
{
return clientLocators.containsKey(locator);
}
}
/**
* called to destroy any cached RemoteClientInvoker copies inside the registry. this method
* must be called when it is determined that a remote server (via the locator) is no
* longer available.
*
* @param locator
*/
public static void destroyClientInvoker(InvokerLocator locator)
{
ClientInvoker invoker = null;
synchronized(clientLock)
{
invoker = (ClientInvoker) clientLocators.remove(locator);
log.debug("destroying client for locator: " + locator + ", invoker:" + invoker + ", remaining list:" + clientLocators);
}
if(invoker != null)
{
invoker.disconnect();
invoker = null;
}
}
/**
* create a ClientInvoker instance, using the specific InvokerLocator, which is just a client-side
* invoker to a remote server. Will use the default configuration values for the transport.
*
* @param locator
* @return
* @throws Exception
*/
public static ClientInvoker createClientInvoker(InvokerLocator locator)
throws Exception
{
return createClientInvoker(locator, null);
}
/**
* create a ClientInvoker instance, using the specific InvokerLocator, which is just a client-side
* invoker to a remote server
*
* @param locator
* @return
* @throws Exception
*/
public static ClientInvoker createClientInvoker(InvokerLocator locator, Map configuration)
throws Exception
{
if(locator == null)
{
throw new NullPointerException("locator cannot be null");
}
synchronized(clientLock)
{
ClientInvoker invoker = (ClientInvoker) clientLocators.get(locator);
if(invoker != null)
{
return invoker;
}
boolean isPassByValue = false;
Map parameters = locator.getParameters();
if(parameters != null)
{
String value = (String) parameters.get(InvokerLocator.BYVALUE);
if(value != null && Boolean.valueOf(value).booleanValue())
{
isPassByValue = true;
}
}
// Check to see if server invoker is local
// If in server locators map, then created locally by this class
ServerInvoker svrInvoker = (ServerInvoker) serverLocators.get(locator);
if(svrInvoker != null && !isPassByValue)
{
LocalClientInvoker localInvoker = new LocalClientInvoker(locator);
// have to set reference to local server invoker so client in invoke directly
localInvoker.setServerInvoker(svrInvoker);
invoker = localInvoker;
InvokerLocator l = invoker.getLocator();
clientLocators.put(l, invoker);
}
else //not local
{
String protocol = locator.getProtocol();
if(protocol == null)
{
throw new NullPointerException("protocol cannot be null for the locator");
}
Class cl = (Class) clientInvokers.get(protocol);
if(cl == null)
{
cl = loadClientInvoker(protocol);
if(cl == null)
{
throw new RuntimeException("Couldn't find valid client invoker class for transport '" + protocol + "'");
}
}
if(configuration != null)
{
Constructor ctor = cl.getConstructor(new Class[]{InvokerLocator.class, Map.class});
invoker = (ClientInvoker) ctor.newInstance(new Object[]{locator, configuration});
}
else
{
Constructor ctor = cl.getConstructor(new Class[]{InvokerLocator.class});
invoker = (ClientInvoker) ctor.newInstance(new Object[]{locator});
}
InvokerLocator l = invoker.getLocator();
clientLocators.put(l, invoker);
}
return invoker;
}
}
private static Class loadClientInvoker(String protocol) throws ClassNotFoundException
{
Class clientInvokerClass = null;
String clientInvokerClassName = (String) clientInvokerFQN.get(protocol);
if(clientInvokerClassName != null)
{
clientInvokerClass = ClassLoaderUtility.loadClass(clientInvokerClassName, InvokerRegistry.class);
}
return clientInvokerClass;
}
private static Class loadServerInvoker(String protocol) throws ClassNotFoundException
{
Class serverInvokerClass = null;
String serverInvokerClassName = (String) serverInvokerFQN.get(protocol);
if(serverInvokerClassName != null)
{
serverInvokerClass = ClassLoaderUtility.loadClass(serverInvokerClassName, InvokerRegistry.class);
}
return serverInvokerClass;
}
/**
* returns true if the server invoker is registered in the local JVM for a given locator/handler pair
*
* @param locator
* @return
*/
public static boolean isServerInvokerRegistered(InvokerLocator locator)
{
synchronized(serverLock)
{
return serverLocators.containsKey(locator);
}
}
/**
* create a ServerInvoker instance, using the specific Invoker locator data and an implementation of the
* ServerInvocationHandler interface. Will use the default configuration values for the transport.
*
* @param locator
* @return
* @throws Exception
*/
public static ServerInvoker createServerInvoker(InvokerLocator locator)
throws Exception
{
return createServerInvoker(locator, null);
}
/**
* create a ServerInvoker instance, using the specific Invoker locator data and an implementation of the
* ServerInvocationHandler interface along with
*
* @param locator
* @return
* @throws Exception
*/
public static ServerInvoker createServerInvoker(InvokerLocator locator, Map configuration)
throws Exception
{
ServerInvoker invoker = null;
synchronized(serverLock)
{
invoker = (ServerInvoker) serverLocators.get(locator);
if(invoker != null)
{
throw new InvalidConfigurationException("The invoker for locator (" + locator + ") is already " +
"in use by another Connector. Either change the locator or " +
"add new handlers to existing Connector.");
}
String protocol = locator.getProtocol();
Class cl = (Class) serverInvokers.get(protocol);
if(cl == null)
{
cl = loadServerInvoker(protocol);
if(cl == null)
{
throw new RuntimeException("Couldn't find valid server invoker class for transport '" + protocol + "'");
}
}
if(configuration != null)
{
Constructor ctor = cl.getConstructor(new Class[]{InvokerLocator.class, Map.class});
invoker = (ServerInvoker) ctor.newInstance(new Object[]{locator, configuration});
}
else
{
Constructor ctor = cl.getConstructor(new Class[]{InvokerLocator.class});
invoker = (ServerInvoker) ctor.newInstance(new Object[]{locator});
}
serverLocators.put(locator, invoker);
registeredLocators.add(invoker.getLocator());
}
return invoker;
}
public static void destroyServerInvoker(ServerInvoker invoker)
{
if(invoker != null)
{
InvokerLocator locator = invoker.getLocator();
unregisterLocator(locator);
if(clientLocators.get(locator) instanceof LocalClientInvoker)
{
ClientInvoker clientInvoker = (ClientInvoker) clientLocators.remove(locator);
if(clientInvoker != null)
{
clientInvoker.disconnect();
}
}
}
}
/**
* This is needed by the ServerInvoker since it may change the port being used (if port specified was <= 0) to
* next available port.
*
* @param locator
* @param newLocator
*/
public static synchronized void updateServerInvokerLocator(InvokerLocator locator, InvokerLocator newLocator)
{
Object si = serverLocators.get(locator);
serverLocators.remove(locator);
registeredLocators.remove(locator);
serverLocators.put(newLocator, si);
registeredLocators.add(newLocator);
}
}