package org.jboss.mx.remote.connector;
import org.jboss.mx.remote.connector.socket.SocketConnectorClient;
import org.jboss.mx.remote.connector.soap.axis.AxisSOAPClient;
import org.jboss.mx.remote.JMXUtil;
import org.jboss.mx.remote.MBeanServerConnection;
import javax.management.MBeanServer;
import java.net.InetAddress;
import java.net.URL;
import java.util.*;
import java.lang.ref.WeakReference;
/**
* ConnectorFactory is for generating an MBeanServer proxy to a remote
* MBeanServer via a specific transport proxy.
*
* @author <a href="telrod@vocalocity.net">Tom Elrod</a>
* @author <a href="jhaynie@vocalocity.net">Jeff Haynie</a>
* @version $Revision: 1.11 $
*/
public class ConnectorFactory
{
private static final String TRANSPORTS [] = {"socket", "soap"};
private static final List classloaders=new ArrayList(2);
private static final Map connectors=new HashMap();
private static final Vector listeners=new Vector();
//JGH: this stuff needs to be refactored out when tom adds his code
// we might consider a MBeanConnection registery?
/**
* return an array of connections that are currently used by the ConnectorFactory
*
* @return mbean server connections
*/
public static MBeanServerConnection[] getConnections ()
{
synchronized(connectors)
{
MBeanServerConnection conn[]=new MBeanServerConnection[connectors.size()];
int c=0;
Iterator iter=connectors.values().iterator();
while(iter.hasNext())
{
Entry entry=(Entry)iter.next();
conn[c++]=entry.conn;
}
return conn;
}
}
public static void addListener (Listener listener)
{
listeners.add(new WeakReference(listener));
}
public static void removeListener (Listener listener)
{
listeners.remove(new WeakReference(listener));
}
/**
* return a collection of classloader
*
* @return classloaders
*/
public static final Collection getClassLoaders ()
{
return classloaders;
}
/**
* add a classloader that is visible by the Connector and associated classes
*
* @param cl
*/
public static void addClassLoader (ClassLoader cl)
{
if (cl!=null)
{
classloaders.add(cl);
}
}
/**
* remove a previously added classloader
*
* @param cl
*/
public static void removeClassLoader (ClassLoader cl)
{
if (cl!=null)
{
classloaders.remove(cl);
}
}
/**
* load a class that is visible by the ConnectorFactory
*
* @param name
* @return loaded class
* @throws ClassNotFoundException
*/
public static Class loadClass (String name)
throws ClassNotFoundException
{
Iterator i = classloaders.iterator();
while(i.hasNext())
{
try
{
return ((ClassLoader)i.next()).loadClass(name);
}
catch (Exception ex)
{
}
}
throw new ClassNotFoundException(name);
}
/**
* get an array of valid transport types for Connectors
*
* @return transport types
*/
public static final String[] getConnectorTransports()
{
return TRANSPORTS;
}
/**
* return the remote MBeanServer proxy for a given remote serverId
*
* @param serverId
* @return mbean server proxy
*/
public static final MBeanServer getMBeanServerForServerId (String serverId)
{
Entry entry=(Entry)connectors.get(serverId);
return (entry==null?null:entry.remote);
}
/**
* return true if the connector factory has a remote mbeanserver registered
*
* @param serverId
* @return true if server has been registered; false otherwise
*/
public static final boolean hasMBeanServerForServerId (String serverId)
{
return connectors.containsKey(serverId);
}
/**
* destroy a previously created connector and allow the connector
* to clean itself up
*
* @param connector
* @throws Exception
*/
private static void destroyConnector(MBeanServer connector)
throws Exception
{
String id = JMXUtil.getServerId(connector);
connectors.remove(id);
if (connector instanceof ConnectorDestroyable)
{
((ConnectorDestroyable) connector).destroyConnector();
}
connector = null;
}
/**
* destroy a connector for a given MBeanServer ID
*
* @param serverId
* @throws Exception
*/
public static void destroyConnector (String serverId)
throws Exception
{
Entry entry=(Entry)connectors.remove(serverId);
if (entry!=null)
{
fireDestroyed(entry.remote,entry.conn);
destroyConnector(entry.remote);
entry=null;
}
}
private static void fireDestroyed (MBeanServer server, MBeanServerConnection conn)
{
synchronized(listeners)
{
Enumeration e=listeners.elements();
while(e.hasMoreElements())
{
WeakReference ref=(WeakReference)e.nextElement();
Listener listener=(Listener)ref.get();
if (listener==null)
{
listeners.remove(ref);
ref=null;
listener=null;
}
else
{
try
{
listener.connectorDestroyed(server,conn);
}
catch (Throwable e1)
{
e1.printStackTrace ();
}
}
}
}
}
private static void fireCreated (MBeanServer server, MBeanServerConnection conn)
{
synchronized(listeners)
{
Enumeration e=listeners.elements();
while(e.hasMoreElements())
{
WeakReference ref=(WeakReference)e.nextElement();
Listener listener=(Listener)ref.get();
if (listener==null)
{
listeners.remove(ref);
ref=null;
listener=null;
}
else
{
try
{
listener.connectorCreated(server,conn);
}
catch (Throwable e1)
{
e1.printStackTrace ();
}
}
}
}
}
private final static class Entry
{
MBeanServer remote;
MBeanServerConnection conn;
final int hashCode;
Entry (MBeanServer remote, MBeanServerConnection conn)
{
this.remote=remote;
this.conn=conn;
this.hashCode = remote.hashCode()+conn.hashCode();
}
public int hashCode ()
{
return hashCode;
}
public boolean equals (Object obj)
{
if (obj instanceof Entry)
{
return hashCode==obj.hashCode();
}
return false;
}
}
/**
* factory method for generating a factory to a remote MBeanServer via a
* specific transport Connector
*
* @param transport
* @param props
* @return mbean server proxy
* @throws Exception
*/
public static MBeanServer createConnector(MBeanServerConnection conn)
throws Exception
{
if(checkValidTransport(conn.getTransport()))
{
MBeanServer remote=null;
synchronized(connectors)
{
Entry entry=(Entry)connectors.get(conn.getServerId());
if (entry!=null)
{
// return the cached copy
return entry.remote;
}
if (conn.getTransport().equals("socket"))
{
// create a new socket connector
remote = createSocketConnector(conn);
}
else if (conn.getTransport().equals("soap"))
{
// create a new axis soap connector
remote = createSOAPConnector(conn);
}
// register
connectors.put(conn.getServerId(),new Entry(remote,conn));
}
fireCreated(remote,conn);
return remote;
}
else
{
throw new IllegalArgumentException("Invalid transport '" + conn.getTransport() + "'");
}
}
/**
* Checks to see if transport string pased is supported by this connector factory
* @param requestedTransport
* @return true if connector factory can create connector with this transport, false if not.
*/
private static boolean checkValidTransport(String requestedTransport)
{
for(int x = 0; x < TRANSPORTS.length; x++)
{
if(TRANSPORTS[x].equals(requestedTransport))
{
return true;
}
}
return false;
}
/**
* create a SocketConnector connector
*
* @param props
* @return mbean server proxy
* @throws Exception
*/
public static MBeanServer createSocketConnector(MBeanServerConnection conn)
throws Exception
{
Map props = conn.getProperties();
String ip = (String)props.get("ipaddress");
String port = (String)props.get("port");
if (ip == null)
{
throw new IllegalArgumentException("Couldn't find the 'ipaddress' property for the socket connector");
}
if (port == null)
{
throw new IllegalArgumentException("Couldn't find the 'port' property for the socket connector");
}
// create a connector to the remote SocketConnector
return SocketConnectorClient.create(conn.getServerId(),InetAddress.getByName(ip), Integer.parseInt(port));
}
public static MBeanServer createSOAPConnector(MBeanServerConnection conn)
throws Exception
{
Map props = conn.getProperties();
String url = (String)props.get("url");
String serverid = (String)props.get("serverid");
if (url == null)
{
throw new IllegalArgumentException("Couldn't find the 'url' property for the soap connector");
}
// create a connector to the remote SOAPConnector
return AxisSOAPClient.create(url, serverid);
}
/**
* given a valid soap URL, create a direct connection via SOAP and return a dynamic proxy
* to the remote SOAP mbean server
*
* @param urn
* @return
* @throws Exception
*/
public static MBeanServer createSOAPConnector (URL urn)
throws Exception
{
// create a direct connector to the remote SOAPConnector
MBeanServer mbs=AxisSOAPClient.create(urn.toExternalForm(), null);
String serverId = (String)mbs.getAttribute(JMXUtil.getMBeanServerObjectName(),"ServerId");
MBeanServerConnection conn=new MBeanServerConnection("soap",serverId,null,InetAddress.getByName(urn.getHost()),null);
connectors.put(serverId,new Entry(mbs,conn));
fireCreated(mbs,conn);
return mbs;
}
/**
* listener for factory events
*
*/
public static interface Listener
{
/**
* event is fired when a connector is created
*
* @param server
* @param serverId
* @param transport
* @param props
*/
public void connectorCreated (MBeanServer server, MBeanServerConnection conn);
/**
* event is fired when a connector is destroyed
*
* @param server
* @param serverId
* @param transport
* @param props
*/
public void connectorDestroyed (MBeanServer server, MBeanServerConnection conn);
}
}