package org.oddjob.jmx.handlers; import java.io.Serializable; import java.lang.reflect.UndeclaredThrowableException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; 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.ReflectionException; import javax.swing.ImageIcon; import org.oddjob.Iconic; import org.oddjob.images.IconEvent; import org.oddjob.images.IconHelper; import org.oddjob.images.IconListener; 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.ServerSideToolkit; /** * A MBean which wraps an object providing an Oddjob management interface to the * object. */ public class IconicHandlerFactory implements ServerInterfaceHandlerFactory<Iconic, Iconic> { public static final HandlerVersion VERSION = new HandlerVersion(3, 0); public static final String ICON_CHANGED_NOTIF_TYPE = "org.oddjob.iconchanged"; static final JMXOperationPlus<Notification[]> SYNCHRONIZE = new JMXOperationPlus<Notification[]>( "iconicSynchronize", "Sychronize Notifications.", Notification[].class, MBeanOperationInfo.INFO); static final JMXOperationPlus<ImageIcon> ICON_FOR = new JMXOperationPlus<ImageIcon>( "Iconic.iconForId", "Retrieve an Icon and ToolTip.", ImageIcon.class, MBeanOperationInfo.INFO) .addParam("iconId", String.class, "The icon id."); /* * (non-Javadoc) * @see org.oddjob.jmx.server.InterfaceInfo#interfaceClass() */ public Class<Iconic> interfaceClass() { return Iconic.class; } /* * (non-Javadoc) * @see org.oddjob.jmx.server.InterfaceInfo#getMBeanAttributeInfo() */ public MBeanAttributeInfo[] getMBeanAttributeInfo() { return new MBeanAttributeInfo[0]; } /* * (non-Javadoc) * @see org.oddjob.jmx.server.InterfaceInfo#getMBeanOperationInfo() */ public MBeanOperationInfo[] getMBeanOperationInfo() { return new MBeanOperationInfo[] { SYNCHRONIZE.getOpInfo(), ICON_FOR.getOpInfo() }; } /* * (non-Javadoc) * @see org.oddjob.jmx.server.InterfaceInfo#getMBeanNotificationInfo() */ public MBeanNotificationInfo[] getMBeanNotificationInfo() { return new MBeanNotificationInfo[] { new MBeanNotificationInfo( new String[] { ICON_CHANGED_NOTIF_TYPE }, Notification.class.getName(), "Icon changed notification.") }; } public ServerInterfaceHandler createServerHandler(Iconic iconic, ServerSideToolkit ojmb) { ServerIconicHelper iconicHelper = new ServerIconicHelper(iconic, ojmb); iconic.addIconListener(iconicHelper); return iconicHelper; } public ClientHandlerResolver<Iconic> clientHandlerFactory() { return new SimpleHandlerResolver<Iconic>( ClientIconicHandlerFactory.class.getName(), VERSION); } public static class ClientIconicHandlerFactory implements ClientInterfaceHandlerFactory<Iconic> { public Class<Iconic> interfaceClass() { return Iconic.class; } public HandlerVersion getVersion() { return VERSION; } public Iconic createClientHandler(Iconic proxy, ClientSideToolkit toolkit) { return new ClientIconicHandler(proxy, toolkit); } } static class ClientIconicHandler implements Iconic { /** Remember the last event so new state listeners can be told it. */ private IconEvent lastEvent; /** listeners */ private final List<IconListener> listeners = new ArrayList<IconListener>(); /** The owner, to be used as the source of the event. */ private final Iconic owner; private final ClientSideToolkit toolkit; private Synchronizer synchronizer; ClientIconicHandler(Iconic proxy, ClientSideToolkit toolkit) { this.owner = proxy; this.toolkit = toolkit; lastEvent = new IconEvent(owner, IconHelper.NULL); } public ImageIcon iconForId(String id) { try { return toolkit.invoke( ICON_FOR, new Object[] { id } ); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } void iconEvent(IconData event) { // The event that comes over the wire has a null source, so create a new one // job node client as the source. IconEvent iconEvent = new IconEvent(owner, event.getIconId()); lastEvent = iconEvent; synchronized (listeners) { for (Iterator<IconListener> it = listeners.iterator(); it.hasNext();) { it.next().iconEvent(iconEvent); } } } public void addIconListener(IconListener listener) { synchronized (this) { if (synchronizer == null) { synchronizer = new Synchronizer( new NotificationListener() { public void handleNotification(Notification notification, Object arg1) { IconData ie = (IconData) notification.getUserData(); iconEvent(ie); } }); toolkit.registerNotificationListener( ICON_CHANGED_NOTIF_TYPE, synchronizer); Notification[] lastNotifications = null; try { lastNotifications = toolkit.invoke(SYNCHRONIZE); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } synchronizer.synchronize(lastNotifications); } IconEvent nowEvent = lastEvent; synchronized (listeners) { listener.iconEvent(nowEvent); listeners.add(listener); } } } /** * Remove a job state listener. * * @param listener The job state listener. */ public void removeIconListener(IconListener listener) { synchronized (this) { listeners.remove(listener); if (listeners.size() == 0) { toolkit.removeNotificationListener(ICON_CHANGED_NOTIF_TYPE, synchronizer); synchronizer = null; } } } } /** * * */ class ServerIconicHelper implements IconListener, ServerInterfaceHandler { private final Iconic iconic; private final ServerSideToolkit toolkit; /** Remember last event. */ private Notification lastNotification; ServerIconicHelper(Iconic iconic, ServerSideToolkit ojmb) { this.iconic = iconic; this.toolkit = ojmb; } public void iconEvent(final IconEvent event) { toolkit.runSynchronized(new Runnable() { public void run() { // send a dummy source accross the wire IconData newEvent = new IconData(event.getIconId()); Notification notification = toolkit.createNotification(ICON_CHANGED_NOTIF_TYPE); notification.setUserData(newEvent); toolkit.sendNotification(notification); lastNotification = notification; } }); } public Notification[] getLastNotifications() { return null; } public Object invoke(RemoteOperation<?> operation, Object[] params) throws MBeanException, ReflectionException { if (ICON_FOR.equals(operation)) { return iconic.iconForId((String) params[0]); } if (SYNCHRONIZE.equals(operation)) { return new Notification[] { lastNotification }; } throw new ReflectionException( new IllegalStateException("invoked for an unknown method."), operation.toString()); } public void destroy() { iconic.removeIconListener(this); } } public static class IconData implements Serializable { private static final long serialVersionUID = 2009062400L; final private String id; /** * Event constructor. * * @param source The source of the event. * @param id The icon id. */ public IconData(String iconId) { this.id = iconId; } /** * Get the variable name. * * @return The variable name. */ public String getIconId() { return id; } } }