com.almende.eve.monitor.ResultMonitorFactory.java Source code

Java tutorial

Introduction

Here is the source code for com.almende.eve.monitor.ResultMonitorFactory.java

Source

/*
 * 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;
    }
}