package org.jboss.jmx.connector.invoker;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.rmi.RemoteException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.security.Principal;
import javax.management.InstanceNotFoundException;
import javax.management.ListenerNotFoundException;
import javax.management.MBeanServer;
import javax.management.Notification;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import org.jboss.invocation.Invocation;
import org.jboss.invocation.MarshalledInvocation;
import org.jboss.jmx.adaptor.rmi.RMINotificationListener;
import org.jboss.jmx.connector.invoker.client.InvokerAdaptorException;
import org.jboss.mx.server.ServerConstants;
import org.jboss.system.Registry;
import org.jboss.system.ServiceMBeanSupport;
public class InvokerAdaptorService
extends ServiceMBeanSupport
implements InvokerAdaptorServiceMBean, ServerConstants
{
private ObjectName mbeanRegistry;
private Map marshalledInvocationMapping = new HashMap();
private Class[] exportedInterfaces;
private HashSet addNotificationListeners = new HashSet();
private HashSet removeNotificationListeners = new HashSet();
protected HashMap remoteListeners = new HashMap();
public InvokerAdaptorService()
{
}
public Class[] getExportedInterfaces()
{
return exportedInterfaces;
}
public void setExportedInterfaces(Class[] exportedInterfaces)
{
this.exportedInterfaces = exportedInterfaces;
}
protected void startService()
throws Exception
{
mbeanRegistry = new ObjectName(MBEAN_REGISTRY);
HashMap tmpMap = new HashMap(61);
for(int n = 0; n < exportedInterfaces.length; n ++)
{
Class iface = exportedInterfaces[n];
Method[] methods = iface.getMethods();
for(int m = 0; m < methods.length; m ++)
{
Method method = methods[m];
Long hash = new Long(MarshalledInvocation.calculateHash(method));
tmpMap.put(hash, method);
}
try
{
Class[] sig = {ObjectName.class, RMINotificationListener.class,
NotificationFilter.class, Object.class};
Method addNotificationListener = iface.getMethod(
"addNotificationListener", sig);
addNotificationListeners.add(addNotificationListener);
}
catch(Exception e)
{
log.debug(iface+"No addNotificationListener(ObjectName, RMINotificationListener)");
}
try
{
Class[] sig = {ObjectName.class, RMINotificationListener.class};
Method removeNotificationListener = iface.getMethod(
"removeNotificationListener", sig);
removeNotificationListeners.add(removeNotificationListener);
}
catch(Exception e)
{
log.debug(iface+"No removeNotificationListener(ObjectName, RMINotificationListener)");
}
}
marshalledInvocationMapping = Collections.unmodifiableMap(tmpMap);
Registry.bind(new Integer(serviceName.hashCode()), serviceName);
}
protected void stopService()
throws Exception
{
if( exportedInterfaces != null )
{
for(int n = 0; n < exportedInterfaces.length; n ++)
MarshalledInvocation.removeHashes(exportedInterfaces[n]);
}
marshalledInvocationMapping = null;
remoteListeners.clear();
Registry.unbind(new Integer(serviceName.hashCode()));
}
public Map getMethodMap()
{
return marshalledInvocationMapping;
}
public Object invoke(Invocation invocation)
throws Exception
{
try
{
ClassLoader oldCL = SecurityActions.getContextClassLoader();
ClassLoader newCL = null;
ObjectName objectName = (ObjectName) invocation.getValue("JMX_OBJECT_NAME");
if (objectName != null)
{
newCL = (ClassLoader) server.invoke
(
mbeanRegistry, "getValue",
new Object[] { objectName, CLASSLOADER },
new String[] { ObjectName.class.getName(), String.class.getName() }
);
}
if (newCL != null && newCL != oldCL)
SecurityActions.setContextClassLoader(newCL);
try
{
if (invocation instanceof MarshalledInvocation)
{
MarshalledInvocation mi = (MarshalledInvocation) invocation;
mi.setMethodMap(marshalledInvocationMapping);
}
Method method = invocation.getMethod();
Object[] args = invocation.getArguments();
Principal principal = invocation.getPrincipal();
Object credential = invocation.getCredential();
Object value = null;
SecurityActions.pushSubjectContext(principal, credential, null);
try
{
if( addNotificationListeners.contains(method) )
{
ObjectName name = (ObjectName) args[0];
RMINotificationListener listener = (RMINotificationListener)
args[1];
NotificationFilter filter = (NotificationFilter) args[2];
Object handback = args[3];
addNotificationListener(name, listener, filter, handback);
}
else if( removeNotificationListeners.contains(method) )
{
ObjectName name = (ObjectName) args[0];
RMINotificationListener listener = (RMINotificationListener)
args[1];
removeNotificationListener(name, listener);
}
else
{
String name = method.getName();
Class[] paramTypes = method.getParameterTypes();
Method mbeanServerMethod = MBeanServer.class.getMethod(name,
paramTypes);
value = mbeanServerMethod.invoke(server, args);
}
}
catch(InvocationTargetException e)
{
Throwable t = e.getTargetException();
if( t instanceof Exception )
throw (Exception) t;
else
throw new UndeclaredThrowableException(t, method.toString());
}
return value;
}
finally
{
SecurityActions.popSubjectContext();
if (newCL != null && newCL != oldCL)
SecurityActions.setContextClassLoader(oldCL);
}
}
catch (Throwable t)
{
throw new InvokerAdaptorException(t);
}
}
public void addNotificationListener(ObjectName name,
RMINotificationListener listener, NotificationFilter filter,
Object handback)
throws InstanceNotFoundException, RemoteException
{
NotificationListenerDelegate delegate =
new NotificationListenerDelegate(listener, name);
remoteListeners.put(listener, delegate);
getServer().addNotificationListener(name, delegate, filter, handback);
}
public void removeNotificationListener(ObjectName name,
RMINotificationListener listener)
throws InstanceNotFoundException, ListenerNotFoundException,
RemoteException
{
NotificationListenerDelegate delegate = (NotificationListenerDelegate)
remoteListeners.remove(listener);
if( delegate == null )
throw new ListenerNotFoundException("No listener matches: "+listener);
getServer().removeNotificationListener(name, delegate);
}
private class NotificationListenerDelegate
implements NotificationListener
{
private RMINotificationListener client;
private ObjectName targetName;
public NotificationListenerDelegate(RMINotificationListener client,
ObjectName targetName)
{
this.client = client;
this.targetName = targetName;
}
public void handleNotification(Notification notification,
Object handback)
{
try
{
if( log.isTraceEnabled() )
{
log.trace("Sending notification to client, event:"+notification);
}
client.handleNotification(notification, handback);
}
catch(Throwable t)
{
log.debug("Failed to notify client, unregistering listener", t);
try
{
removeNotificationListener(targetName, client);
}
catch(Exception e)
{
log.debug("Failed to unregister listener", e);
}
}
}
}
}