Java tutorial
/* * Copyright: Almende B.V. (2014), Rotterdam, The Netherlands * License: The Apache Software License, Version 2.0 */ package com.almende.eve.monitor; import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import com.almende.eve.agent.AgentInterface; import com.almende.eve.agent.annotation.EventTriggered; import com.almende.eve.rpc.annotation.Access; import com.almende.eve.rpc.annotation.AccessType; import com.almende.eve.rpc.annotation.Name; import com.almende.eve.rpc.annotation.Optional; import com.almende.eve.rpc.annotation.Sender; import com.almende.eve.rpc.jsonrpc.JSONRPC; import com.almende.eve.rpc.jsonrpc.JSONRPCException; import com.almende.eve.rpc.jsonrpc.JSONRequest; import com.almende.eve.rpc.jsonrpc.JSONResponse; import com.almende.eve.rpc.jsonrpc.jackson.JOM; import com.almende.eve.state.TypedKey; import com.almende.util.AnnotationUtil.AnnotatedMethod; import com.almende.util.NamespaceUtil; import com.almende.util.NamespaceUtil.CallTuple; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.type.TypeFactory; /** * A factory for creating ResultMonitor objects. */ public class ResultMonitorFactory implements ResultMonitorFactoryInterface { private static final Logger LOG = Logger.getLogger(ResultMonitorFactory.class.getCanonicalName()); private static final TypedKey<HashMap<String, ResultMonitor>> MONITORS = new TypedKey<HashMap<String, ResultMonitor>>( "_monitors") { }; private AgentInterface myAgent = null; /** * Instantiates a new result monitor factory. * * @param agent * the agent */ public ResultMonitorFactory(final AgentInterface agent) { myAgent = agent; } /* * (non-Javadoc) * * @see * com.almende.eve.monitor.ResultMonitorFactoryInterface#create(java.lang * .String, java.net.URI, java.lang.String, * com.fasterxml.jackson.databind.node.ObjectNode, java.lang.String, * com.almende.eve.monitor.ResultMonitorConfigType[]) */ @Override public String create(final String monitorId, final URI url, final String method, final ObjectNode params, final String callbackMethod, final ResultMonitorConfigType... confs) { final ResultMonitor old = getMonitorById(monitorId); if (old != null) { old.cancel(); } final ResultMonitor monitor = new ResultMonitor(monitorId, myAgent.getId(), url, method, params, callbackMethod); for (final ResultMonitorConfigType config : confs) { monitor.add(config); } return store(monitor); } /* * (non-Javadoc) * * @see * com.almende.eve.monitor.ResultMonitorFactoryInterface#getResult(java. * lang.String, com.fasterxml.jackson.databind.node.ObjectNode, * java.lang.Class) */ @Override public <T> T getResult(final String monitorId, final ObjectNode filterParms, final Class<T> returnType) throws IOException, JSONRPCException { return getResult(monitorId, filterParms, JOM.getTypeFactory().constructSimpleType(returnType, new JavaType[0])); } /* * (non-Javadoc) * * @see * com.almende.eve.monitor.ResultMonitorFactoryInterface#getResult(java. * lang.String, com.fasterxml.jackson.databind.node.ObjectNode, * com.fasterxml.jackson.databind.JavaType) */ @Override @SuppressWarnings("unchecked") public <T> T getResult(final String monitorId, final ObjectNode filterParms, final JavaType returnType) throws JSONRPCException, IOException { T result = null; final ResultMonitor monitor = getMonitorById(monitorId); if (monitor != null) { if (monitor.hasCache() && monitor.getCache() != null && monitor.getCache().filter(filterParms)) { result = (T) monitor.getCache().getValue(); } if (result == null) { result = myAgent.send(monitor.getUrl(), monitor.getMethod(), JOM.getInstance().readTree(monitor.getParams()), returnType); if (monitor.hasCache()) { monitor.getCache().store(result); } } } else { LOG.severe("Failed to find monitor!" + monitorId); } return result; } /* * (non-Javadoc) * * @see * com.almende.eve.monitor.ResultMonitorFactoryInterface#cancel(java.lang * .String) */ @Override public void cancel(final String monitorId) { final ResultMonitor monitor = getMonitorById(monitorId); if (monitor != null) { monitor.cancel(); delete(monitor.getId()); } else { LOG.warning("Trying to cancel non existing monitor:" + myAgent.getId() + "." + monitorId); } } /* * (non-Javadoc) * * @see * com.almende.eve.monitor.ResultMonitorFactoryInterface#doPoll(java.lang * .String) */ @Access(AccessType.SELF) @Override public final void doPoll(@Name("monitorId") final String monitorId) throws JSONRPCException, IOException { final ResultMonitor monitor = getMonitorById(monitorId); if (monitor != null) { if (monitor.getUrl() == null || monitor.getMethod() == null) { LOG.warning("Monitor data invalid:" + monitor); } final Object result = myAgent.send(monitor.getUrl(), monitor.getMethod(), JOM.getInstance().readTree(monitor.getParams()), TypeFactory.unknownType()); if (monitor.getCallbackMethod() != null) { final ObjectNode params = JOM.createObjectNode(); params.put("result", JOM.getInstance().writeValueAsString(result)); myAgent.send(URI.create("local:" + myAgent.getId()), monitor.getCallbackMethod(), params); } if (monitor.hasCache()) { monitor.getCache().store(result); } } } // TODO: doesn't work! /** The last res. */ private JsonNode lastRes = null; /* * (non-Javadoc) * * @see * com.almende.eve.monitor.ResultMonitorFactoryInterface#doPush(java.lang * .String, com.fasterxml.jackson.databind.node.ObjectNode) */ @Access(AccessType.SELF) @Override public final void doPush(@Name("pushKey") final String pushKey, @Optional @Name("params") final ObjectNode triggerParams) throws JSONRPCException, IOException { if (myAgent.getState().containsKey(pushKey)) { final ObjectNode pushParams = (ObjectNode) JOM.getInstance() .readTree(myAgent.getState().get(pushKey, String.class)).get("config"); if (!(pushParams.has("method") && pushParams.has("params"))) { throw new JSONRPCException("Missing push configuration fields:" + pushParams); } final String method = pushParams.get("method").textValue(); final ObjectNode params = (ObjectNode) JOM.getInstance().readTree(pushParams.get("params").textValue()); final JSONResponse res = JSONRPC.invoke(myAgent, new JSONRequest(method, params), myAgent); final JsonNode result = res.getResult(); if (pushParams.has("onChange") && pushParams.get("onChange").asBoolean()) { if (lastRes != null && lastRes.equals(result)) { return; } lastRes = result; } final ObjectNode parms = JOM.createObjectNode(); parms.put("result", result); parms.put("pushId", pushParams.get("pushId").textValue()); parms.put("callbackParams", triggerParams == null ? pushParams : pushParams.putAll(triggerParams)); String callbackMethod = "monitor.callbackPush"; if (pushParams.has("callback")) { callbackMethod = pushParams.get("callback").textValue(); } myAgent.sendAsync(URI.create(pushParams.get("url").textValue()), callbackMethod, parms, null, Void.class); // TODO: If callback reports "old", unregisterPush(); } } /* * (non-Javadoc) * * @see * com.almende.eve.monitor.ResultMonitorFactoryInterface#callbackPush(java * .lang.Object, java.lang.String, * com.fasterxml.jackson.databind.node.ObjectNode) */ @Access(AccessType.PUBLIC) @Override public final void callbackPush(@Name("result") final Object result, @Name("pushId") final String pushId, @Name("callbackParams") final ObjectNode callbackParams) throws JSONRPCException { // TODO: THis is unclean! final String[] ids = pushId.split("_"); if (ids.length != 2) { throw new JSONRPCException("PushId is invalid!"); } final String monitorId = ids[0]; try { final ResultMonitor monitor = getMonitorById(monitorId); if (monitor != null) { if (monitor.getCallbackMethod() != null) { ObjectNode params = JOM.createObjectNode(); if (callbackParams != null) { params = callbackParams; } params.put("result", JOM.getInstance().writeValueAsString(result)); myAgent.send(URI.create("local:" + myAgent.getId()), monitor.getCallbackMethod(), params); } if (monitor.hasCache()) { monitor.getCache().store(result); } } else { LOG.severe("Couldn't find local monitor by id:" + monitorId); } } catch (final Exception e) { LOG.log(Level.WARNING, "Couldn't run local callbackMethod for push!" + monitorId, e); } } /* * (non-Javadoc) * * @see * com.almende.eve.monitor.ResultMonitorFactoryInterface#registerPush(java * .lang.String, com.fasterxml.jackson.databind.node.ObjectNode, * java.lang.String) */ @Access(AccessType.PUBLIC) @Override public final void registerPush(@Name("pushId") final String id, @Name("config") final ObjectNode pushParams, @Sender final String senderUrl) { final String pushKey = "_push_" + senderUrl + "_" + id; pushParams.put("url", senderUrl); pushParams.put("pushId", id); if (myAgent.getState().containsKey(pushKey)) { LOG.warning("reregistration of existing push, canceling old version."); try { unregisterPush(id, senderUrl); } catch (final Exception e) { LOG.warning("Failed to unregister push:" + e); } } final ObjectNode result = JOM.createObjectNode(); result.put("config", pushParams); final ObjectNode params = JOM.createObjectNode(); params.put("pushKey", pushKey); LOG.info("Register Push:" + pushKey); if (pushParams.has("interval")) { final int interval = pushParams.get("interval").intValue(); final JSONRequest request = new JSONRequest("monitor.doPush", params); result.put("taskId", myAgent.getScheduler().createTask(request, interval, true, false)); } String event = ""; if (pushParams.has("event")) { // Event param overrules event = pushParams.get("event").textValue(); } if (pushParams.has("onChange") && pushParams.get("onChange").booleanValue()) { event = "change"; try { final CallTuple res = NamespaceUtil.get(myAgent, pushParams.get("method").textValue()); final AnnotatedMethod method = res.getMethod(); final EventTriggered annotation = method.getAnnotation(EventTriggered.class); if (annotation != null) { // If no Event param, get it from annotation, else // use default. event = annotation.value(); } } catch (final Exception e) { LOG.log(Level.WARNING, "", e); } } if (!event.equals("")) { try { result.put("subscriptionId", myAgent.getEventsFactory().subscribe(myAgent.getFirstUrl(), event, "monitor.doPush", params)); } catch (final Exception e) { LOG.log(Level.WARNING, "Failed to register push Event", e); } } myAgent.getState().put(pushKey, result.toString()); } /* * (non-Javadoc) * * @see * com.almende.eve.monitor.ResultMonitorFactoryInterface#unregisterPush( * java.lang.String, java.lang.String) */ @Access(AccessType.PUBLIC) @Override public final void unregisterPush(@Name("pushId") final String id, @Sender final String senderUrl) throws IOException { ObjectNode config = null; if (myAgent.getState() != null && myAgent.getState().containsKey("_push_" + senderUrl + "_" + id)) { config = (ObjectNode) JOM.getInstance() .readTree(myAgent.getState().get("_push_" + senderUrl + "_" + id, String.class)); } if (config == null) { return; } if (config.has("taskId") && myAgent.getScheduler() != null) { final String taskId = config.get("taskId").textValue(); myAgent.getScheduler().cancelTask(taskId); } if (config.has("subscriptionId")) { try { myAgent.getEventsFactory().unsubscribe(myAgent.getFirstUrl(), config.get("subscriptionId").textValue()); } catch (final Exception e) { LOG.severe("Failed to unsubscribe push:" + e); } } } /* * (non-Javadoc) * * @see * com.almende.eve.monitor.ResultMonitorFactoryInterface#store(com.almende * .eve.monitor.ResultMonitor) */ @Override public String store(final ResultMonitor monitor) { try { final Map<String, ResultMonitor> monitors = myAgent.getState().get(MONITORS); final HashMap<String, ResultMonitor> newmonitors = new HashMap<String, ResultMonitor>(); if (monitors != null) { newmonitors.putAll(monitors); } newmonitors.put(monitor.getId(), monitor); if (!myAgent.getState().putIfUnchanged(MONITORS.getKey(), newmonitors, monitors)) { // recursive retry. store(monitor); } } catch (final Exception e) { LOG.log(Level.WARNING, "Couldn't find monitors:" + myAgent.getId() + "." + monitor.getId(), e); } return monitor.getId(); } /* * (non-Javadoc) * * @see * com.almende.eve.monitor.ResultMonitorFactoryInterface#delete(java.lang * .String) */ @Override public void delete(final String monitorId) { try { final Map<String, ResultMonitor> monitors = myAgent.getState().get(MONITORS); final Map<String, ResultMonitor> newmonitors = new HashMap<String, ResultMonitor>(); if (monitors != null) { newmonitors.putAll(monitors); } newmonitors.remove(monitorId); if (!myAgent.getState().putIfUnchanged(MONITORS.getKey(), newmonitors, monitors)) { // recursive retry. delete(monitorId); } } catch (final Exception e) { LOG.log(Level.WARNING, "Couldn't delete monitor:" + myAgent.getId() + "." + monitorId, e); } } /* * (non-Javadoc) * * @see * com.almende.eve.monitor.ResultMonitorFactoryInterface#getMonitorById( * java.lang.String) */ @Override public ResultMonitor getMonitorById(final String monitorId) { try { Map<String, ResultMonitor> monitors = myAgent.getState().get(MONITORS); if (monitors == null) { monitors = new HashMap<String, ResultMonitor>(); } final ResultMonitor result = monitors.get(monitorId); if (result != null) { result.init(); } return result; } catch (final Exception e) { LOG.log(Level.WARNING, "Couldn't find monitor:" + myAgent.getId() + "." + monitorId, e); } return null; } /* * (non-Javadoc) * * @see com.almende.eve.monitor.ResultMonitorFactoryInterface#cancelAll() */ @Override public void cancelAll() { for (final ResultMonitor monitor : getMonitors()) { delete(monitor.getId()); } } /* * (non-Javadoc) * * @see com.almende.eve.monitor.ResultMonitorFactoryInterface#getMonitors() */ @Access(AccessType.PUBLIC) @Override public List<ResultMonitor> getMonitors() { try { Map<String, ResultMonitor> monitors = myAgent.getState().get(MONITORS); if (monitors == null) { monitors = new HashMap<String, ResultMonitor>(); } final List<ResultMonitor> result = new ArrayList<ResultMonitor>(monitors.size()); result.addAll(monitors.values()); return result; } catch (final Exception e) { LOG.log(Level.WARNING, "Couldn't find monitors.", e); } return null; } }