com.almende.eve.protocol.jsonrpc.JSONRpcProtocol.java Source code

Java tutorial

Introduction

Here is the source code for com.almende.eve.protocol.jsonrpc.JSONRpcProtocol.java

Source

/*
 * Copyright: Almende B.V. (2014), Rotterdam, The Netherlands
 * License: The Apache Software License, Version 2.0
 */
package com.almende.eve.protocol.jsonrpc;

import java.net.URI;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.almende.eve.capabilities.handler.Handler;
import com.almende.eve.protocol.Protocol;
import com.almende.eve.protocol.auth.Authorizor;
import com.almende.eve.protocol.auth.DefaultAuthorizor;
import com.almende.eve.protocol.jsonrpc.annotation.Sender;
import com.almende.eve.protocol.jsonrpc.formats.JSONMessage;
import com.almende.eve.protocol.jsonrpc.formats.JSONRPCException;
import com.almende.eve.protocol.jsonrpc.formats.JSONRequest;
import com.almende.eve.protocol.jsonrpc.formats.JSONResponse;
import com.almende.eve.protocol.jsonrpc.formats.RequestParams;
import com.almende.util.URIUtil;
import com.almende.util.callback.AsyncCallback;
import com.almende.util.callback.AsyncCallbackQueue;
import com.almende.util.jackson.JOM;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

/**
 * The Class JSONRpcProtocol.
 */
public class JSONRpcProtocol implements Protocol {
    private static final Logger LOG = Logger.getLogger(JSONRpcProtocol.class.getName());
    private static final RequestParams EVEREQUESTPARAMS = new RequestParams();
    static {
        EVEREQUESTPARAMS.put(Sender.class, URIUtil.create("local:null"));
    }
    private Authorizor auth = new DefaultAuthorizor();
    private final AsyncCallbackQueue<JSONResponse> callbacks = new AsyncCallbackQueue<JSONResponse>();
    private final Handler<Object> destination;
    private JSONRpcProtocolConfig myParams;

    /**
     * Instantiates a new JSON rpc protocol.
     * 
     * @param params
     *            the params
     * @param handle
     *            the handle
     */
    public JSONRpcProtocol(final ObjectNode params, final Handler<Object> handle) {
        destination = handle;
        myParams = JSONRpcProtocolConfig.decorate(params);
        callbacks.setDefTimeout(myParams.getCallbackTimeout());
    }

    @Override
    public Meta inbound(final Object msg, final URI senderUrl) {
        final JSONResponse response = invoke(msg, senderUrl);
        return new Meta(response, response == null, response != null);

    }

    public Meta outbound(final Object msg, final URI recipientUrl) {
        if (msg instanceof JSONRequest) {
            final JSONRequest request = (JSONRequest) msg;
            addCallback(request, request.getCallback());
        }
        return new Meta(msg);
    }

    /**
     * Gets the auth.
     * 
     * @return the auth
     */
    public Authorizor getAuth() {
        return auth;
    }

    /**
     * Sets the auth.
     * 
     * @param auth
     *            the new auth
     */
    public void setAuth(final Authorizor auth) {
        this.auth = auth;
    }

    /**
     * Convert incoming message object to JSONMessage if possible. Returns null
     * if the message can't be interpreted as a JSONMessage.
     * 
     * @param msg
     *            the msg
     * @return the JSON message
     */
    public static JSONMessage jsonConvert(final Object msg) {
        JSONMessage jsonMsg = null;
        if (msg == null) {
            LOG.warning("Message null!");
            return null;
        }
        try {
            if (msg instanceof JSONMessage) {
                jsonMsg = (JSONMessage) msg;
            } else {
                ObjectNode json = null;
                if (msg instanceof String) {
                    final String message = (String) msg;
                    if (message.startsWith("{") || message.trim().startsWith("{")) {

                        json = (ObjectNode) JOM.getInstance().readTree(message);
                    }
                } else if (msg instanceof ObjectNode || (msg instanceof JsonNode && ((JsonNode) msg).isObject())) {
                    json = (ObjectNode) msg;
                } else {
                    LOG.info("Message unknown type:" + msg.getClass());
                }
                if (json != null) {
                    if (JSONRpc.isResponse(json)) {
                        final JSONResponse response = new JSONResponse(json);
                        jsonMsg = response;
                    } else if (JSONRpc.isRequest(json)) {
                        final JSONRequest request = new JSONRequest(json);
                        jsonMsg = request;
                    } else {
                        LOG.info("Message contains valid JSON, but is not JSON-RPC:" + json);
                    }
                }
            }
        } catch (final Exception e) {
            LOG.log(Level.WARNING, "Message triggered exception in trying to convert it to a JSONMessage.", e);
        }
        return jsonMsg;
    }

    /**
     * Invoke this RPC msg.
     * 
     * @param msg
     *            the msg
     * @param senderUrl
     *            the sender url
     * @return the JSON response
     */
    public JSONResponse invoke(final Object msg, final URI senderUrl) {
        final JSONMessage jsonMsg = jsonConvert(msg);
        if (jsonMsg == null) {
            LOG.log(Level.INFO, "Received non-JSONRPC message:'" + msg + "'");
            return null;
        }
        final JsonNode id = jsonMsg.getId();
        try {
            if (jsonMsg.isRequest()) {
                final JSONRequest request = (JSONRequest) jsonMsg;
                final RequestParams params = new RequestParams();
                params.put(Sender.class, senderUrl);
                return JSONRpc.invoke(destination.get(), request, params, auth);
            } else if (jsonMsg.isResponse() && callbacks != null && id != null && !id.isNull()) {
                final AsyncCallback<JSONResponse> callback = callbacks.pull(id);
                if (callback != null) {
                    final JSONResponse response = (JSONResponse) jsonMsg;
                    final JSONRPCException error = response.getError();
                    if (error != null) {
                        callback.onFailure(error);
                    } else {
                        callback.onSuccess(response);
                    }
                }
            }
        } catch (final Exception e) {
            // generate JSON error response, skipped if it was an incoming
            // notification i.s.o. request.
            final JSONRPCException jsonError = new JSONRPCException(JSONRPCException.CODE.INTERNAL_ERROR,
                    e.getMessage(), e);
            LOG.log(Level.WARNING, "Exception in receiving message", jsonError);
            if (id != null) {
                final JSONResponse response = new JSONResponse(jsonError);
                response.setId(id);
                return response;
            }
        }
        return null;
    }

    /**
     * Gets the methods.
     * 
     * @return the methods
     */
    public List<Object> getMethods() {
        return JSONRpc.describe(getHandle().get(), EVEREQUESTPARAMS, auth);
    }

    private <T> void addCallback(final JSONRequest request, final AsyncCallback<T> asyncCallback) {
        if (asyncCallback == null || request.getId() == null || request.getId().isNull()) {
            return;
        }

        // Create a callback to retrieve a JSONResponse and extract the result
        // or error from this. This is double nested, mostly because of the type
        // conversions required on the result.
        final AsyncCallback<JSONResponse> responseCallback = new AsyncCallback<JSONResponse>() {
            @Override
            public void onSuccess(final JSONResponse response) {
                final Exception err = response.getError();
                if (err != null) {
                    asyncCallback.onFailure(err);
                }
                if (asyncCallback.getType() != null
                        && !asyncCallback.getType().getJavaType().getRawClass().equals(Void.class)) {
                    try {
                        final T res = asyncCallback.getType().inject(response.getResult());
                        asyncCallback.onSuccess(res);
                    } catch (final ClassCastException cce) {
                        asyncCallback.onFailure(new JSONRPCException(
                                "Incorrect return type received for JSON-RPC call:" + request.getMethod(), cce));
                    }

                } else {
                    asyncCallback.onSuccess(null);
                }
            }

            @Override
            public void onFailure(final Exception exception) {
                asyncCallback.onFailure(exception);
            }
        };

        if (callbacks != null) {
            callbacks.push(((JSONMessage) request).getId(), request.toString(), responseCallback);
        }
    }

    /**
     * Gets the handle.
     * 
     * @return the handle
     */
    public Handler<Object> getHandle() {
        return destination;
    }

    /*
     * (non-Javadoc)
     * @see com.almende.eve.capabilities.Capability#getParams()
     */
    @Override
    public ObjectNode getParams() {
        return myParams;
    }

    @Override
    public void delete() {
        callbacks.clear();
        JSONRpcProtocolBuilder.delete(myParams.getId());
    }

}