package org.oddjob.jmx.client;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.management.InstanceNotFoundException;
import javax.management.JMException;
import javax.management.MBeanException;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import org.apache.log4j.Logger;
import org.oddjob.jmx.RemoteOperation;
import org.oddjob.jmx.Utils;
/**
* Implementation of {@link ClientSideToolkit}.
*
* @author rob
*
*/
class ClientSideToolkitImpl implements ClientSideToolkit {
private static final Logger logger = Logger.getLogger(ClientSideToolkitImpl.class);
private final static int ACTIVE = 0;
private final static int DESTROYED = 3;
private volatile int phase = ACTIVE;
private final ClientSessionImpl clientSession;
private ObjectName objectName;
private final Map<String, NotificationListener> notifications
= new LinkedHashMap<String, NotificationListener>();
/** The listener that listens for all JMX notifications. */
private final ClientListener clientListener;
public ClientSideToolkitImpl(ObjectName objectName,
ClientSessionImpl clientSession) throws InstanceNotFoundException, IOException {
this.clientSession = clientSession;
this.objectName = objectName;
clientListener = new ClientListener();
clientSession.getServerConnection().addNotificationListener(objectName,
clientListener, null, null);
}
@SuppressWarnings("unchecked")
public <T> T invoke(RemoteOperation<T> remote, Object... args) throws Throwable {
Object[] exported = Utils.export(args);
Object result = null;
try {
result = clientSession.getServerConnection().invoke(
objectName,
remote.getActionName(),
exported,
remote.getSignature());
} catch (ReflectionException e) {
throw e.getTargetException();
} catch (MBeanException e) {
throw e.getTargetException();
}
return (T) Utils.importResolve(result, clientSession);
}
public void registerNotificationListener(String eventType, NotificationListener notificationListener) {
notifications.put(eventType, notificationListener);
}
public void removeNotificationListener(String eventType,
NotificationListener notificationListener) {
notifications.remove(eventType);
}
public ClientSession getClientSession() {
return clientSession;
}
/**
* Destroy this node. Clean up resources, remove remote connections.
*/
void destroy() {
phase = DESTROYED;
// beware the order here.
// notifications removed first
try {
// will fail if destroy is due to the remote node being removed.
if (clientListener != null) {
clientSession.getServerConnection().removeNotificationListener(objectName,
clientListener);
}
} catch (JMException e) {
logger.debug(e);
} catch (IOException e) {
logger.debug(e);
}
logger.debug("Destroyed client for [" + toString() + "]");
}
@Override
public String toString() {
return "Client: " + objectName;
}
/**
* Member class which listens for notifications coming
* across the network.
*
*/
class ClientListener implements NotificationListener {
// do notifications always come on one thread? should we synchronze just in case they don't?
public void handleNotification(
final Notification notification,
final Object object) {
String type = notification.getType();
logger.debug("Handling notification [" + type + "] sequence [" +
notification.getSequenceNumber() + "] for [" +
ClientSideToolkitImpl.this.toString() + "]");
if (phase == DESTROYED) {
logger.debug("Ignoring notification as client destroyed [" +
ClientSideToolkitImpl.this.toString() + "]");
return;
}
final NotificationListener listener =
(NotificationListener) notifications.get(type);
if (listener != null) {
Runnable r = new Runnable() {
public void run() {
try {
listener.handleNotification(notification, object);
} catch (Exception e) {
// this will happen when the remote node disappears
logger.debug(e);
}
}
};
clientSession.getNotificationProcessor().submit(r);
}
} // handleNotification
@Override
public String toString() {
return ClientSideToolkitImpl.this.toString();
}
}
}