/* * (c) Rob Gordon 2005 */ package org.oddjob.jmx.handlers; import java.io.Serializable; import java.lang.reflect.UndeclaredThrowableException; import java.util.LinkedList; import javax.management.JMException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanException; import javax.management.MBeanNotificationInfo; import javax.management.MBeanOperationInfo; import javax.management.ObjectName; import javax.management.ReflectionException; import org.apache.commons.beanutils.DynaBean; import org.apache.log4j.Logger; import org.oddjob.Stateful; import org.oddjob.framework.JobDestroyedException; import org.oddjob.input.InputRequest; 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.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.jobs.tasks.Task; import org.oddjob.jobs.tasks.TaskException; import org.oddjob.jobs.tasks.TaskExecutor; import org.oddjob.jobs.tasks.TaskView; import org.oddjob.state.StateEvent; import org.oddjob.state.StateListener; public class TaskExecutorHandlerFactory implements ServerInterfaceHandlerFactory<TaskExecutor, TaskExecutor> { private static final Logger logger = Logger.getLogger(TaskExecutorHandlerFactory.class); public static final HandlerVersion VERSION = new HandlerVersion(1, 0); static final JMXOperationPlus<InputRequest[]> GET_PARAMETER_INFO = new JMXOperationPlus<>( "Tasks.getParameterInfo", "Get parameter info for a task executor.", InputRequest[].class, MBeanOperationInfo.INFO); static final JMXOperationPlus<TaskViewData> EXECUTE = new JMXOperationPlus<>( "Tasks.execute", "Execute a Task.", TaskViewData.class, MBeanOperationInfo.ACTION) .addParam("task", Task.class, "The task."); public Class<TaskExecutor> interfaceClass() { return TaskExecutor.class; } public MBeanAttributeInfo[] getMBeanAttributeInfo() { return new MBeanAttributeInfo[0]; } public MBeanOperationInfo[] getMBeanOperationInfo() { return new MBeanOperationInfo[] { GET_PARAMETER_INFO.getOpInfo(), EXECUTE.getOpInfo() }; } public MBeanNotificationInfo[] getMBeanNotificationInfo() { MBeanNotificationInfo[] nInfo = new MBeanNotificationInfo[] {}; return nInfo; } public ServerInterfaceHandler createServerHandler( TaskExecutor taskExecutor, ServerSideToolkit ojmb) { ServerTaskExecutorHelper structuralHelper = new ServerTaskExecutorHelper (taskExecutor, ojmb); return structuralHelper; } public ClientHandlerResolver<TaskExecutor> clientHandlerFactory() { return new SimpleHandlerResolver<TaskExecutor>( ClientTaskExecutorHandlerFactory.class.getName(), VERSION); } public static class ClientTaskExecutorHandlerFactory implements ClientInterfaceHandlerFactory<TaskExecutor> { public Class<TaskExecutor> interfaceClass() { return TaskExecutor.class; } public HandlerVersion getVersion() { return VERSION; } public TaskExecutor createClientHandler(TaskExecutor proxy, ClientSideToolkit toolkit) { return new ClientTaskExecutorHandler(proxy, toolkit); } } static class ClientTaskExecutorHandler implements TaskExecutor { private final ClientSideToolkit toolkit; ClientTaskExecutorHandler(TaskExecutor proxy, ClientSideToolkit toolkit) { this.toolkit = toolkit; } @Override public InputRequest[] getParameterInfo() { try { return toolkit.invoke( GET_PARAMETER_INFO, new Object[] { } ); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } @Override public TaskView execute(Task task) throws TaskException { try { TaskViewData taskViewData = toolkit.invoke( EXECUTE, new Object[] { task } ); Object taskViewProxy = toolkit.getClientSession().create( taskViewData.objectName); return new TaskViewAdaptor(taskViewProxy); } catch (Throwable e) { throw new UndeclaredThrowableException(e); } } } public static class TaskViewAdaptor implements TaskView { private final Stateful proxy; public TaskViewAdaptor(Object proxy) { if (!(proxy instanceof DynaBean)) { throw new ClassCastException("Proxy is not a DynaBean"); } this.proxy = (Stateful) proxy; } @Override public void addStateListener(StateListener listener) throws JobDestroyedException { proxy.addStateListener(listener); } @Override public void removeStateListener(StateListener listener) { proxy.removeStateListener(listener); } @Override public StateEvent lastStateEvent() { return proxy.lastStateEvent(); } @Override public Object getTaskResponse() { return ((DynaBean) proxy).get("taskResponse"); } } class ServerTaskExecutorHelper implements ServerInterfaceHandler { private final TaskExecutor taskExecutor; private final ServerSideToolkit toolkit; /** Child remote job nodes. */ private final LinkedList<ObjectName> taskViews = new LinkedList<>(); ServerTaskExecutorHelper(TaskExecutor taskExecutor, ServerSideToolkit ojmb) { this.taskExecutor = taskExecutor; this.toolkit = ojmb; } public Object invoke(RemoteOperation<?> operation, Object[] params) throws MBeanException, ReflectionException { if (GET_PARAMETER_INFO.equals(operation)) { return taskExecutor.getParameterInfo(); } else if (EXECUTE.equals(operation)) { TaskView taskView; try { taskView = taskExecutor.execute((Task) params[0]); } catch (TaskException e) { throw new MBeanException(e); } return createTaskViewMBean(taskView); } throw new ReflectionException( new IllegalStateException("invoked for an unknown method."), operation.toString()); } protected TaskViewData createTaskViewMBean(TaskView taskView) { final ObjectName objectName; try { objectName = toolkit.getServerSession().createMBeanFor( taskView, toolkit.getContext().addChild(taskView)); } catch (ServerLoopBackException | JMException e) { throw new IllegalStateException("Faild creating Task View MBean.", e); } taskViews.add(objectName); taskView.addStateListener(new StateListener() { @Override public void jobStateChange(StateEvent event) { if (event.getState().isDestroyed()) { taskViews.remove(objectName); destroyTaskViewMBean(objectName); } } }); return new TaskViewData(objectName); } public void destroy() { // destroy the task view MBeans. while (!taskViews.isEmpty()) { ObjectName taskViewObjectName = taskViews.remove(); destroyTaskViewMBean(taskViewObjectName); } } protected void destroyTaskViewMBean(ObjectName taskViewObjectName) { try { toolkit.getServerSession().destroy(taskViewObjectName); } catch (JMException e1) { logger.error("Failed destroying child [" + taskViewObjectName + "]", e1); } } } static class TaskViewData implements Serializable { private static final long serialVersionUID = 2015051200L; private final ObjectName objectName; public TaskViewData(ObjectName objectName) { this.objectName = objectName; } public ObjectName getTaskViewObjectName() { return objectName; } } }