/*
* (c) Rob Gordon 2005
*/
package org.oddjob.jmx.handlers;
import java.io.Serializable;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import javax.management.JMException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanException;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import org.apache.log4j.Logger;
import org.oddjob.Structural;
import org.oddjob.jmx.RemoteOperation;
import org.oddjob.jmx.client.ClientHandlerResolver;
import org.oddjob.jmx.client.ClientInterfaceHandlerFactory;
import org.oddjob.jmx.client.ClientSideToolkit;
import org.oddjob.jmx.client.HandlerVersion;
import org.oddjob.jmx.client.SimpleHandlerResolver;
import org.oddjob.jmx.client.Synchronizer;
import org.oddjob.jmx.server.JMXOperationPlus;
import org.oddjob.jmx.server.ServerInterfaceHandler;
import org.oddjob.jmx.server.ServerInterfaceHandlerFactory;
import org.oddjob.jmx.server.ServerLoopBackException;
import org.oddjob.jmx.server.ServerSideToolkit;
import org.oddjob.structural.ChildHelper;
import org.oddjob.structural.ChildMatch;
import org.oddjob.structural.StructuralEvent;
import org.oddjob.structural.StructuralListener;
public class StructuralHandlerFactory
implements ServerInterfaceHandlerFactory<Structural, Structural> {
private static final Logger logger = Logger.getLogger(StructuralHandlerFactory.class);
public static final HandlerVersion VERSION = new HandlerVersion(1, 0);
public static final String STRUCTURAL_NOTIF_TYPE = "org.oddjob.structural";
static final JMXOperationPlus<Notification[]> SYNCHRONIZE =
new JMXOperationPlus<Notification[]>(
"structuralSynchronize",
"Synchronize Notifications.",
Notification[].class,
MBeanOperationInfo.INFO);
public Class<Structural> interfaceClass() {
return Structural.class;
}
public MBeanAttributeInfo[] getMBeanAttributeInfo() {
return new MBeanAttributeInfo[0];
}
public MBeanOperationInfo[] getMBeanOperationInfo() {
return new MBeanOperationInfo[] {
SYNCHRONIZE.getOpInfo()
};
}
public MBeanNotificationInfo[] getMBeanNotificationInfo() {
MBeanNotificationInfo[] nInfo = new MBeanNotificationInfo[] {
new MBeanNotificationInfo(new String[] {
STRUCTURAL_NOTIF_TYPE },
Notification.class.getName(), "Structural notification.")};
return nInfo;
}
public ServerInterfaceHandler createServerHandler(
Structural structural,
ServerSideToolkit ojmb) {
ServerStructuralHelper structuralHelper =
new ServerStructuralHelper (structural, ojmb);
return structuralHelper;
}
public ClientHandlerResolver<Structural> clientHandlerFactory() {
return new SimpleHandlerResolver<Structural>(
ClientStructuralHandlerFactory.class.getName(),
VERSION);
}
public static class ClientStructuralHandlerFactory
implements ClientInterfaceHandlerFactory<Structural> {
public Class<Structural> interfaceClass() {
return Structural.class;
}
public HandlerVersion getVersion() {
return VERSION;
}
public Structural createClientHandler(Structural proxy, ClientSideToolkit toolkit) {
return new ClientStructuralHandler(proxy, toolkit);
}
}
static class ClientStructuralHandler
implements Structural {
private final Structural proxy;
/** Helper class to handle structure change */
private ChildHelper<Object> structuralHelper;
private final ClientSideToolkit toolkit;
private Synchronizer synchronizer;
private List<ObjectName> childNames;
ClientStructuralHandler(Structural proxy, ClientSideToolkit toolkit) {
this.proxy = proxy;
this.toolkit = toolkit;
}
/*
* Add a structural listener. From the Structural interface.
*/
public void addStructuralListener(StructuralListener listener) {
synchronized (this) {
if (structuralHelper == null) {
this.structuralHelper = new ChildHelper<Object>(proxy);
this.childNames = new ArrayList<ObjectName>();
synchronizer = new Synchronizer(
new NotificationListener() {
public void handleNotification(Notification notification, Object arg1) {
ChildData childData = (ChildData) notification.getUserData();
new ChildMatch<ObjectName>(childNames) {
protected void insertChild(int index, ObjectName childName) {
Object childProxy = toolkit.getClientSession().create(childName);
// child proxy will be null if the toolkit can't create it.
if (childProxy != null) {
structuralHelper.insertChild(index, childProxy);
}
};
@Override
protected void removeChildAt(int index) {
Object child = structuralHelper.removeChildAt(index);
toolkit.getClientSession().destroy(child);
}
}.match(childData.getChildObjectNames());
}
});
toolkit.registerNotificationListener(
STRUCTURAL_NOTIF_TYPE, synchronizer);
Notification[] lastNotifications = null;
try {
lastNotifications = toolkit.invoke(SYNCHRONIZE);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
synchronizer.synchronize(lastNotifications);
}
}
structuralHelper.addStructuralListener(listener);
}
/*
* Remove a structural listener. From the Structural interface.
*/
public void removeStructuralListener(StructuralListener listener) {
synchronized (this) {
if (structuralHelper != null) {
structuralHelper.removeStructuralListener(listener);
if (structuralHelper.isNoListeners()) {
toolkit.removeNotificationListener(STRUCTURAL_NOTIF_TYPE, synchronizer);
synchronizer = null;
structuralHelper = null;
}
}
}
}
}
class ServerStructuralHelper implements ServerInterfaceHandler {
private final Structural structural;
private final ServerSideToolkit toolkit;
/** True if we've looped back onto an already exposed server. */
private boolean duplicate;
/** Child remote job nodes. */
private final LinkedList<ObjectName> children = new LinkedList<ObjectName>();
private final StructuralListener listener = new StructuralListener() {
/*
* (non-Javadoc)
*
* @see org.oddjob.structural.StructuralListener#childAdded(org.oddjob.structural.StructuralEvent)
*/
public void childAdded(final StructuralEvent e) {
// stop events overlapping.
final ObjectName child;
Object childComponent = e.getChild();
try {
child = toolkit.getServerSession().createMBeanFor(
childComponent, toolkit.getContext().addChild(childComponent));
} catch (ServerLoopBackException e1) {
logger.info("Server loopback detected.");
duplicate = true;
return;
} catch (JMException e2) {
logger.error("Failed creating child for [" + childComponent + "]", e2);
return;
}
final int index = e.getIndex();
ChildData newEvent = null;
synchronized (children) {
children.add(index, child);
newEvent = new ChildData(
children.toArray(new ObjectName[children.size()]));
}
final Notification notification =
toolkit.createNotification(STRUCTURAL_NOTIF_TYPE);
notification.setUserData(newEvent);
toolkit.runSynchronized(new Runnable() {
public void run() {
toolkit.sendNotification(notification);
}
});
logger.debug("Child added [" + e.getChild().toString() + "], index [" + e.getIndex() + "]");
}
/*
* (non-Javadoc)
*
* @see org.oddjob.structural.StructuralListener#childRemoved(org.oddjob.structural.StructuralEvent)
*/
public void childRemoved(final StructuralEvent e) {
if (duplicate) {
// a duplicate was never added.
return;
}
final int index = e.getIndex();
ObjectName child = null;
ChildData newEvent = null;
synchronized(children) {
child = children.get(index);
children.remove(index);
newEvent = new ChildData(
children.toArray(new ObjectName[children.size()]));
}
final Notification notification =
toolkit.createNotification(STRUCTURAL_NOTIF_TYPE);
notification.setUserData(newEvent);
toolkit.runSynchronized(new Runnable() {
public void run() {
toolkit.sendNotification(notification);
}
});
try {
toolkit.getServerSession().destroy(child);
} catch (JMException e1) {
logger.error("Failed destroying child [" + e.getChild() + "]", e1);
}
logger.debug("Child removed [" + e.getChild().toString() + "], index [" + e.getIndex() + "]");
}
};
ServerStructuralHelper(Structural structural,
ServerSideToolkit ojmb) {
this.structural = structural;
this.toolkit = ojmb;
structural.addStructuralListener(listener);
}
private Notification[] lastNotifications() {
final Notification[] lastNotifications = new Notification[1];
toolkit.runSynchronized(new Runnable() {
public void run() {
ChildData newEvent = new ChildData(
children.toArray(new ObjectName[children.size()]));
Notification notification =
toolkit.createNotification(STRUCTURAL_NOTIF_TYPE);
notification.setUserData(newEvent);
lastNotifications[0] = notification;
}
});
return lastNotifications;
}
public Object invoke(RemoteOperation<?> operation, Object[] params) throws MBeanException, ReflectionException {
if (SYNCHRONIZE.equals(operation)) {
return lastNotifications();
}
throw new ReflectionException(
new IllegalStateException("invoked for an unknown method."),
operation.toString());
}
public void destroy() {
// Stop receiving event from Oddjob
structural.removeStructuralListener(listener);
// And use our listener to remove children.
while (children.size() > 0) {
final int index = children.size() - 1;
StructuralEvent dummyEvent = new StructuralEvent(structural,
new Object() {
@Override
public String toString() {
return "Dummy Child " + index +
" of Destructing Parent [" +
structural + "]";
}
}, index);
listener.childRemoved(dummyEvent);
}
}
}
static class ChildData implements Serializable {
private static final long serialVersionUID = 2010062500L;
private final ObjectName[] objectNames;
public ChildData(ObjectName[] objectName) {
this.objectNames = objectName;
}
public ObjectName[] getChildObjectNames() {
return objectNames;
}
}
}