Package com.almende.eve.transport.xmpp

Source Code of com.almende.eve.transport.xmpp.AgentConnection$JSONRPCListener

package com.almende.eve.transport.xmpp;

import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;

import com.almende.eve.agent.AgentFactory;
import com.almende.eve.agent.annotation.Sender;
import com.almende.eve.rpc.RequestParams;
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.transport.AsyncCallback;
import com.almende.eve.transport.AsyncCallbackQueue;
import com.fasterxml.jackson.databind.node.ObjectNode;

public class AgentConnection {
  private AgentFactory agentFactory = null;
  private String agentId = null;
  private String username = null;
  private String resource = null;
  private XMPPConnection conn = null;
  private AsyncCallbackQueue<JSONResponse> callbacks =
      new AsyncCallbackQueue<JSONResponse>()
 
  public AgentConnection (AgentFactory agentFactory) {
    this.agentFactory = agentFactory;
  }
 
  /**
   * Get the id of the agent linked to this connection
   * @return agentId
   */
  public String getAgentId() {
    return agentId;
  }
 
  /**
   * Get the username of the connection (without host)
   * @return username
   */
  public String getUsername() {
    return username;
  }
 
  /**
   * Get the resource of the connection. Returns null if no resource is set
   * @return resource
   */
  public String getResource() {
    return resource;
  }
  /**
   * Login and connect the agent to the messaging service
   * @param agentId
   * @param host
   * @param port
   * @param serviceName
   * @param username
   * @param password
   * @param resource    optional
   * @throws Exception
   */
  public void connect(String agentId, String host, Integer port,
      String serviceName, String username, String password,
      String resource) throws Exception {
    this.agentId = agentId;
    this.username = username;
    this.resource = resource;
   
    try {
      // configure and connect
      ConnectionConfiguration connConfig =
          new ConnectionConfiguration(host, port, serviceName);
      conn = new XMPPConnection(connConfig);
      conn.connect();

      // login
      if (resource == null) {
        conn.login(username, password);
      }
      else {
        conn.login(username, password, resource);
      }

      // set presence to available
      Presence presence = new Presence(Presence.Type.available);
      conn.sendPacket(presence);
     
      // set acceptance to all
      conn.getRoster().setSubscriptionMode(Roster.SubscriptionMode.accept_all);
     
      // instantiate a packet listener
      conn.addPacketListener(new JSONRPCListener(conn, agentFactory,
          agentId, callbacks), null);           
    } catch (XMPPException err) {
      err.printStackTrace();
      throw new Exception("Failed to connect to messenger");
    }
  }
 
  /**
   * Disconnect the agent from the messaging service
   */
  public void disconnect() {
    if (conn != null) {
      conn.disconnect();
      conn = null;
    }
    callbacks.clear();
  }

  /**
   * Check whether the agent is connected to the messaging service
   * @return connected
   */
  public boolean isConnected() {
    return (conn != null) ? conn.isConnected() : false;
  }

  /**
   * Send a message to an other agent
   * @param username
   * @param message
   * @throws Exception
   */
  public void send (String username, JSONRequest request,
      AsyncCallback<JSONResponse> callback) throws Exception {
    if (isConnected()) {
      // create a unique id
      final String id = (String) request.getId();
     
      // queue the response callback
      callbacks.push(id, callback);
     
      // System.out.println("send username=" + username + ", request=" + request); // TODO: cleanup
     
      // send the message
      Message reply = new Message();
      reply.setTo(username);
      reply.setBody(request.toString());
      conn.sendPacket(reply);
    }
    else {
      throw new Exception("Cannot send request, not connected");
    }
  }
 
  /**
   * A class to listen for incoming JSON-RPC messages.
   * The listener will invoke the JSON-RPC message on the agent and
   * reply the result.
   */
  private static class JSONRPCListener implements PacketListener {
    private XMPPConnection conn = null;
    private AgentFactory agentFactory = null;
    private String agentId = null;
    private AsyncCallbackQueue<JSONResponse> callbacks = null;

    public JSONRPCListener (XMPPConnection conn, AgentFactory agentFactory,
        String agentId, AsyncCallbackQueue<JSONResponse> callbacks) {
      this.conn = conn;
      this.agentFactory = agentFactory;
      this.agentId = agentId;
      this.callbacks = callbacks;
    }

    /**
     * Check if given json object contains all fields required for a
     * json-rpc request (id, method, params)
     * @param json
     * @return
     */
    private boolean isRequest(ObjectNode json) {
      return json.has("method");
    }

    /**
     * Check if given json object contains all fields required for a
     * json-rpc response (id, result or error)
     * @param json
     * @return
     */
    private boolean isResponse(ObjectNode json) {
      return (json.has("result") || json.has("error"));
    }
   
    /**
     * process an incoming xmpp message.
     * If the message contains a valid JSON-RPC request or response,
     * the message will be processed.
     * @param packet
     */
    public void processPacket(Packet packet) {
      Message message = (Message)packet;
      String body = message.getBody();
      // System.out.println("recieve from=" + message.getFrom() + " to=" + message.getTo() + " body=" + body); // TODO: cleanup
     
      if (body != null && body.startsWith("{") || body.trim().startsWith("{")) {
        // the body contains a JSON object
        ObjectNode json = null;
        try {
          json = JOM.getInstance().readValue(body, ObjectNode.class);
          if (isResponse(json)) {
            // this is a response
            // Find and execute the corresponding callback
            String id = json.has("id") ? json.get("id").asText() : null;
            AsyncCallback<JSONResponse> callback =
                (id != null) ? callbacks.pull(id) : null;
            if (callback != null) {
              callback.onSuccess(new JSONResponse(body));
            }
            else {
              /*
              // TODO: is it needed to send this error back?
              // can possibly result in weird loops?
              throw new Exception("Callback with id '" + id + "' not found");
              */
            }
          }
          else if (isRequest(json)) {
            // this is a request
            String senderUrl = message.getFrom();
            JSONRequest request = new JSONRequest(json);
            invoke(senderUrl, request);
          }
          else {
            throw new Exception("Request does not contain a valid JSON-RPC request or response");
          }
        }
        catch (Exception err) {
          // generate JSON error response
          JSONRPCException jsonError = new JSONRPCException(
              JSONRPCException.CODE.INTERNAL_ERROR, err.getMessage());
          JSONResponse response = new JSONResponse(jsonError);
         
          // send exception as response
          Message reply = new Message();
          reply.setTo(message.getFrom());
          reply.setBody(response.toString());
          conn.sendPacket(reply);
        }
      }
    }
 
    /**
     * Invoke a JSON-RPC request
     * Invocation is done in a separate thread to prevent blocking the
     * single threaded XMPP PacketListener (which can cause deadlocks).
     * @param senderUrl
     * @param request
     */
    private void invoke (final String senderUrl, final JSONRequest request) {
      new Thread(new Runnable () {
        @Override
        public void run() {
          JSONResponse response;
          try {
            // append the sender to the request parameters
            RequestParams params = new RequestParams();
            params.put(Sender.class, senderUrl);

            // invoke the agent
            response = agentFactory.invoke(agentId, request, params);
          } catch (Exception err) {
            // generate JSON error response
            JSONRPCException jsonError = new JSONRPCException(
                JSONRPCException.CODE.INTERNAL_ERROR, err.getMessage());
            response = new JSONResponse(jsonError);
          }
         
          if (response != null) {
            //String from = StringUtils.parseBareAddress(senderUrl);
            Message reply = new Message();
            reply.setTo(senderUrl);
            reply.setBody(response.toString());
            conn.sendPacket(reply);
          }
        }
      }).start();   
    }
  }
}

TOP

Related Classes of com.almende.eve.transport.xmpp.AgentConnection$JSONRPCListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.