Package com.almende.eve.transport.zmq

Source Code of com.almende.eve.transport.zmq.ZmqTransport

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

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.zeromq.ZMQ.Socket;

import com.almende.eve.capabilities.handler.Handler;
import com.almende.eve.transport.AbstractTransport;
import com.almende.eve.transport.Receiver;
import com.almende.eve.transport.TransportService;
import com.almende.eve.transport.tokens.TokenRet;
import com.almende.eve.transport.tokens.TokenStore;
import com.almende.util.ObjectCache;
import com.almende.util.callback.AsyncCallback;
import com.almende.util.callback.AsyncCallbackQueue;
import com.almende.util.callback.SyncCallback;
import com.almende.util.jackson.JOM;
import com.almende.util.threads.ThreadPool;

/**
* The Class ZmqTransport.
*/
public class ZmqTransport extends AbstractTransport {
  private static final Logger            LOG          = Logger.getLogger(ZmqTransport.class
                                        .getCanonicalName());
  private final String              zmqUrl;
  private Thread                  listeningThread;
  private boolean                  doesAuthentication  = false;
  private boolean                  doDisconnect    = false;
  private static final AsyncCallbackQueue<String>  CALLBACKS      = new AsyncCallbackQueue<String>();
  private final TokenStore tokenstore = new TokenStore();
  private final List<String>            protocols      = Arrays.asList("zmq");

 
  /**
   * Instantiates a new zmq transport.
   *
   * @param config
   *            the config
   * @param handle
   *            the handle
   * @param service
   *            the service
   */
  public ZmqTransport(final ZmqTransportConfig config,
      final Handler<Receiver> handle, final TransportService service) {
    super(config.getAddress(), handle, service, config);
    zmqUrl = super.getAddress().toString().replaceFirst("^zmq:/?/?", "");
    doesAuthentication = config.getDoAuthentication();
  }
 
  /**
   * Send async.
   *
   * @param zmqType
   *            the zmq type
   * @param token
   *            the token
   * @param receiverUrl
   *            the receiver url
   * @param message
   *            the message
   * @param tag
   *            the tag
   */
  public void sendAsync(final byte[] zmqType, final String token,
      final URI receiverUrl, final byte[] message, final String tag) {
    final String senderUrl = super.getAddress().toString();
    ThreadPool.getPool().execute(new Runnable() {
      @Override
      public void run() {
        final String addr = receiverUrl.toString().replaceFirst(
            "zmq:/?/?", "");
        final Socket socket = ZMQ.getSocket(org.zeromq.ZMQ.PUSH);
        try {
          socket.connect(addr);
          socket.send(zmqType, org.zeromq.ZMQ.SNDMORE);
          socket.send(senderUrl, org.zeromq.ZMQ.SNDMORE);
          socket.send(token, org.zeromq.ZMQ.SNDMORE);
          socket.send(message, 0);
         
        } catch (final Exception e) {
          LOG.log(Level.WARNING, "Failed to send JSON through ZMQ", e);
        }
        socket.setTCPKeepAlive(-1);
        socket.setLinger(-1);
        socket.close();
      }
    });
  }
 
  /*
   * (non-Javadoc)
   *
   * @see com.almende.eve.transport.Transport#send(java.net.URI,
   * java.lang.String, java.lang.String)
   */
  @Override
  public void send(final URI receiverUri, final String message,
      final String tag) throws IOException {
    // Check and deliver local shortcut.
    if (sendLocal(receiverUri, message)) {
      return;
    }
    sendAsync(ZMQ.NORMAL, tokenstore.create().toString(), receiverUri,
        message.getBytes(), tag);
  }
 
  /*
   * (non-Javadoc)
   *
   * @see com.almende.eve.transport.Transport#send(java.net.URI, byte[],
   * java.lang.String)
   */
  @Override
  public void send(final URI receiverUri, final byte[] message,
      final String tag) throws IOException {
    if (sendLocal(receiverUri, message)) {
      return;
    }
    sendAsync(ZMQ.NORMAL, tokenstore.create().toString(), receiverUri,
        message, tag);
  }
 
  /*
   * (non-Javadoc)
   *
   * @see com.almende.eve.transport.Transport#connect()
   */
  @Override
  public void connect() throws IOException {
    if (listeningThread != null) {
      disconnect();
    }
    listen();
  }
 
  /*
   * (non-Javadoc)
   *
   * @see com.almende.eve.transport.Transport#disconnect()
   */
  @Override
  public void disconnect() {
    doDisconnect = true;
    listeningThread.interrupt();
  }
 
  /*
   * (non-Javadoc)
   *
   * @see com.almende.eve.transport.Transport#getProtocols()
   */
  @Override
  public List<String> getProtocols() {
    return protocols;
  }
 
  /**
   * Gets the request.
   *
   * @param socket
   *            the socket
   * @return the request
   */
  private ByteBuffer[] getRequest(final Socket socket) {
    final byte[] res = socket.recv();
    final ByteBuffer[] result = new ByteBuffer[4];
    if (res != null) {
      result[0] = ByteBuffer.wrap(res);
      result[1] = ByteBuffer.wrap(socket.recv());
      result[2] = ByteBuffer.wrap(socket.recv());
      result[3] = ByteBuffer.wrap(socket.recv());
    }
    return result;
   
  }
 
  /**
   * process an incoming zmq message.
   * If the message contains a valid JSON-RPC request or response,
   * the message will be processed.
   *
   */
  public void listen() {
    listeningThread = new Thread(new Runnable() {
      @Override
      public void run() {
        final Socket socket = ZMQ.getSocket(org.zeromq.ZMQ.PULL);
        socket.bind(zmqUrl);
        while (true) {
          try {
            final ByteBuffer[] msg = getRequest(socket);
           
            if (msg[0] != null) {
              handleMsg(msg);
              continue;
            }
            if (doDisconnect) {
              socket.disconnect(zmqUrl);
              doDisconnect = false;
              return;
            }
          } catch (final Exception e) {
            LOG.log(Level.SEVERE, "Caught error:", e);
          }
        }
      }
    });
    listeningThread.start();
  }
 
  /**
   * Handle msg.
   *
   * @param msg
   *            the msg
   * @throws ClassNotFoundException
   *             the class not found exception
   * @throws InstantiationException
   *             the instantiation exception
   * @throws IllegalAccessException
   *             the illegal access exception
   * @throws InvocationTargetException
   *             the invocation target exception
   * @throws NoSuchMethodException
   *             the no such method exception
   * @throws IOException
   *             Signals that an I/O exception has occurred.
   * @throws URISyntaxException
   */
  private void handleMsg(final ByteBuffer[] msg)
      throws ClassNotFoundException, InstantiationException,
      IllegalAccessException, InvocationTargetException,
      NoSuchMethodException, IOException, URISyntaxException {
   
    // Receive
    // ZMQ.NORMAL|senderUrl|tokenJson|body
    // ZMQ.HANDSHAKE|senderUrl|tokenJson|timestamp
    // ZMQ.HANDSHAKE_RESPONSE|senderUrl|tokenJson|null
   
    final URI senderUrl = new URI(new String(msg[1].array()));
    final TokenRet token = JOM.getInstance().readValue(msg[2].array(),
        TokenRet.class);
    final String body = new String(msg[3].array());
    final String key = senderUrl + ":" + token.getToken();
   
    if (Arrays.equals(msg[0].array(), ZMQ.HANDSHAKE)) {
      // Reply token corresponding to timestamp.
      final String res = tokenstore.get(body);
      sendAsync(ZMQ.HANDSHAKE_RESPONSE, res, senderUrl, res.getBytes(),
          null);
      return;
    } else if (Arrays.equals(msg[0].array(), ZMQ.HANDSHAKE_RESPONSE)) {
      // post response to callback for handling by other thread
      final AsyncCallback<String> callback = CALLBACKS.pull(key);
      if (callback != null) {
        callback.onSuccess(body);
      } else {
        LOG.warning("Received ZMQ.HANDSHAKE_RESPONSE for unknown handshake..."
            + senderUrl + " : " + token);
      }
      return;
    } else {
      final ObjectCache sessionCache = ObjectCache.get("ZMQSessions");
      if (!sessionCache.containsKey(key) && doesAuthentication) {
        final SyncCallback<String> callback = new SyncCallback<String>(){};
        CALLBACKS.push(key, "", callback);
        sendAsync(ZMQ.HANDSHAKE, token.toString(), senderUrl, token
            .getTime().getBytes(), null);
       
        String retToken = null;
        try {
          retToken = callback.get();
        } catch (final Exception e) {
        }
        if (token.getToken().equals(retToken)) {
          sessionCache.put(key, true);
        } else {
          LOG.warning("Failed to complete handshake!");
          return;
        }
      }
    }
   
    if (body != null) {
      super.getHandle().get().receive(body, senderUrl, null);
    }
  }
 
}
TOP

Related Classes of com.almende.eve.transport.zmq.ZmqTransport

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.