Package com.almende.eve.transport.xmpp

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

package com.almende.eve.transport.xmpp;

import java.util.logging.Level;
import java.util.logging.Logger;

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.AgentHost;
import com.almende.eve.rpc.RequestParams;
import com.almende.eve.rpc.annotation.Sender;
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 static final Logger          LOG      = Logger.getLogger(AgentConnection.class
                                  .getCanonicalName());
  private AgentHost              agentHost  = 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(AgentHost agentHost) {
    this.agentHost = agentHost;
  }
 
  /**
   * 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 JSONRPCException
   */
  public void connect(String agentId, String host, Integer port,
      String serviceName, String username, String password,
      String resource) throws JSONRPCException {
   
    if (isConnected()) {
      // this is a reconnect.
      disconnect();
    }
    this.agentId = agentId;
    this.username = username;
    this.resource = resource;
   
    try {
      // configure and connect
      ConnectionConfiguration connConfig = new ConnectionConfiguration(
          host, port, serviceName);
     
      connConfig.setSASLAuthenticationEnabled(true);
      connConfig.setReconnectionAllowed(true);
      connConfig.setCompressionEnabled(true);
      connConfig.setRosterLoadedAtLogin(false);
      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, agentHost,
          agentId, resource, callbacks), null);
    } catch (XMPPException e) {
      LOG.log(Level.WARNING, "", e);
      throw new JSONRPCException("Failed to connect to messenger", e);
    }
  }
 
  /**
   * Disconnect the agent from the messaging service
   */
  public void disconnect() {
    if (isConnected()) {
      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 JSONRPCException
   */
  public void send(String username, JSONRequest request,
      AsyncCallback<JSONResponse> callback) throws JSONRPCException {
    try {
      if (isConnected()) {
        // create a unique id
        final String id = (String) request.getId();
       
       
        String description = username + " -> "+ request.getMethod();
        // queue the response callback
        callbacks.push(id, description, callback);
       
        // 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");
      }
    } catch (Exception e) {
      throw new JSONRPCException("Failed to send RPC through XMPP.", e);
    }
  }
 
  /**
   * 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 AgentHost              host      = null;
    private String                agentId      = null;
    private AsyncCallbackQueue<JSONResponse>  callbacks    = null;
    private String                resource    = null;
   
    public JSONRPCListener(XMPPConnection conn, AgentHost agentHost,
        String agentId, String resource, AsyncCallbackQueue<JSONResponse> callbacks) {
      this.conn = conn;
      this.host = agentHost;
      this.agentId = agentId;
      this.callbacks = callbacks;
      this.resource = resource;
    }
   
    /**
     * 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;
     
      //Check if resource is given and matches local resource. If not equal, silently drop packet.
      String to = message.getTo();
      if (resource != null && to != null){
        int index = to.indexOf('/');
        if (index > 0){
          String resource = to.substring(index+1);
          if (!this.resource.equals(resource)){
            LOG.warning("Received stanza meant for another agent, disregarding.");
            return;
          }
        }
      }
      String body = message.getBody();
     
      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 if (isRequest(json)) {
            // this is a request
            String senderUrl = "xmpp:"+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 e) {
          // generate JSON error response
          JSONRPCException jsonError = new JSONRPCException(
              JSONRPCException.CODE.INTERNAL_ERROR,
              e.getMessage(), e);
          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 = host.receive(agentId, request,
                params);
          } catch (Exception err) {
            // generate JSON error response
            JSONRPCException jsonError = new JSONRPCException(
                JSONRPCException.CODE.INTERNAL_ERROR,
                err.getMessage(), err);
            response = new JSONResponse(jsonError);
          }
         
          if (response != null) {
            Message reply = new Message();
            String sender = senderUrl.replaceFirst("xmpps?:", "");
            reply.setTo(sender);
            reply.setBody(response.toString());
            conn.sendPacket(reply);
          } else {
            LOG.severe("XMPP response is null? This shouldn't happen...");
          }
        }
      }).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.