iter = sockets.iterator();
while (iter.hasNext()) {
final Socket socket = iter.next();
iter.remove();
if (socket != null) {
poller.unregister(socket);
socket.close();
// call back the actor to inform that a socket has been closed.
actor.closed(socket);
}
}
// let the actor decide if the stage restarts a new double
return actor.destroyed(context, pipe, poller);
}
@Override
public boolean events(SelectableChannel channel, int events)
{
// TODO dispatch events from channels
return true;
}
// an event has occurred on a registered socket
@Override
public boolean events(final Socket socket, final int events)
{
if (socket != pipe) {
// Process a stage message, time to play
return actor.stage(socket, pipe, poller, events);
}
else {
// Process a control message, time to update your playing
return actor.backstage(pipe, poller, events);
}
}
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZAgent.java 0000664 0000000 0000000 00000016653 14557711263 0021633 0 ustar 00root root 0000000 0000000 package org.zeromq;
import java.io.IOException;
import java.nio.channels.Selector;
import java.util.Arrays;
import org.zeromq.ZMQ.Socket;
/**
* First implementation of an agent for a remotely controlled background service for ØMQ.
* Used in conjunction with a ZStar, but not mandatory.
*
* An agent is a mechanism allowing to send messages from one thread to another, and to receive messages from this other thread.
*
* Its built-in implementation provides an easy communication-lock
* system that will close the access once the remote thread is finished.
*
* Are proposed for you a restrained set of simple but powerful messaging commands for a quick learning curve
* and an access to the underlying Socket for advanced usage.
*/
// agent for a remote controlled background message processing API for 0MQ.
// contract to be agent of a star
public interface ZAgent
{
/**
* Receives a control message sent from the Plateau in the Corbeille.
* The call is blocking.
*
* @return the received message or null if the context was shut down.
*/
ZMsg recv();
/**
* Receives a control message sent from the Plateau in the Corbeille.
* The call times out if there is no message after the elapsed time.
*
* @param timeout the timeout in milliseconds before returning null.
* @return the received message or null if the context was shut down or if no message after the timeout.
*/
ZMsg recv(int timeout);
/**
* Receives a control message sent from the Plateau in the Corbeille.
* The call is blocking depending on the parameter.
*
* @param wait true to make a blocking call, false to not wait, and possibly return null
* @return the received message or null if the context was shut down or if there is no message when not blocking.
*/
ZMsg recv(boolean wait);
/**
* Sends a control message from the Corbeille to the Plateau.
*
* @param message the message to send
* @return true if the message was sent, otherwise false (if the distant Star is dead for example)
*/
boolean send(ZMsg message);
/**
* Sends a control message from Corbeille side to the Plateau side.
*
* @param msg the message to send
* @param destroy true to destroy the message after sending it.
* @return true if the message was sent, otherwise false (if the distant Star is dead for example)
*/
boolean send(ZMsg msg, boolean destroy);
/**
* Sends a control message from the Corbeille to the Plateau side.
*
* @param word the message to send
* @return true if the message was sent, otherwise false (if the distant Star is dead for example)
*/
boolean send(String word);
/**
* Sends a control message from the Corbeille to the Plateau side.
*
* @param word the message to send
* @param more true to send more strings in a single message
* @return true if the message was sent, otherwise false (if the distant Star is dead for example)
*/
boolean send(String word, boolean more);
/**
* Gives a sign if the distant Star is here.
*
* @return true if here, otherwise false
*/
boolean sign();
/**
* Returns the socket used for communication.
* For advanced usage.
*
* @return the socket used to communicate with the distant Star.
*/
Socket pipe();
/**
* Closes the pipe.
*/
void close();
class Creator
{
private Creator()
{
super();
}
public static ZAgent create(Socket pipe, String lock)
{
return new SimpleAgent(pipe, lock);
}
}
/**
* Creates a very simple agent with an easy lock mechanism.
*/
final class SimpleAgent implements ZAgent
{
// the pipe used for communicating with the star
private final Socket pipe;
// the key used to lock the agent.
private final byte[] lock;
// the locked state.
private boolean locked;
/**
* Creates a new simple agent.
*
* @param pipe the pipe used to send control messages to the distant IStar.
* @param lock the lock to use. If null, the locking mechanism is omitted.
*/
public SimpleAgent(Socket pipe, String lock)
{
this.pipe = pipe;
this.lock = lock == null ? null : lock.getBytes(ZMQ.CHARSET);
}
@Override
public boolean sign()
{
return !locked;
}
@Override
public void close()
{
locked = true;
pipe.close();
}
@Override
public ZMsg recv()
{
return recv(true);
}
@Override
public ZMsg recv(int timeout)
{
final int old = pipe.getReceiveTimeOut();
pipe.setReceiveTimeOut(timeout);
ZMsg msg = recv(true);
pipe.setReceiveTimeOut(old);
return msg;
}
@Override
public ZMsg recv(boolean wait)
{
if (locked) {
return null;
}
try {
ZMsg msg = ZMsg.recvMsg(pipe, wait);
if (msg == null) {
return null;
}
if (msg.size() == 1) {
final ZFrame frame = msg.peek();
byte[] key = frame.getData();
if (lock != null && Arrays.equals(lock, key)) {
locked = true;
// this is the last message anyway, and not a one for a public display
msg = null;
pipe.close();
}
}
return msg;
}
catch (ZMQException e) {
locked = true;
return null;
}
}
@Override
public boolean send(ZMsg message)
{
if (locked) {
return false;
}
return message.send(pipe);
}
@Override
public boolean send(String word)
{
if (locked) {
return false;
}
return pipe.send(word);
}
@Override
public boolean send(String word, boolean more)
{
if (locked) {
return false;
}
return pipe.send(word, more ? ZMQ.SNDMORE : 0);
}
@Override
public boolean send(ZMsg msg, boolean destroy)
{
if (locked) {
return false;
}
return msg.send(pipe, destroy);
}
@Override
public Socket pipe()
{
return pipe;
}
}
/**
* Creates a selector and destroys it.
* @deprecated
*/
// Contract for selector creation.
// will be called in backstage side.
@Deprecated
interface SelectorCreator
{
/**
* Creates and opens a selector.
*
* @return the opened selector.
* @throws IOException
*/
Selector create() throws IOException;
/**
* Destroys the previously opened selector.
*
* @param selector the selector to close
* @throws IOException
*/
void destroy(Selector selector) throws IOException;
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZAuth.java 0000664 0000000 0000000 00000071071 14557711263 0021471 0 ustar 00root root 0000000 0000000 package org.zeromq;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;
import org.zeromq.ZMQ.Socket;
import org.zeromq.ZMQ.Socket.Mechanism;
import org.zeromq.util.ZMetadata;
import zmq.util.Objects;
/**
* A ZAuth actor takes over authentication for all incoming connections in
* its context. You can whitelist or blacklist peers based on IP address,
* and define policies for securing PLAIN, CURVE, and GSSAPI connections.
*
* Note that libzmq provides four levels of security: default NULL (which ZAuth
* does not see), and authenticated NULL, PLAIN, and CURVE, which ZAuth can see.
*
* Based on zauth.c in czmq
*/
public class ZAuth implements Closeable
{
public interface Auth
{
/**
* Configures with ad-hoc message.
* @param msg the configuration message.
* @param verbose true if the actor is verbose.
* @return true if correctly configured, otherwise false.
*/
boolean configure(ZMsg msg, boolean verbose);
/**
* Callback for authorizing a connection.
* @param request
* @param verbose
* @return true if the connection is authorized, false otherwise.
*/
boolean authorize(ZapRequest request, boolean verbose);
}
public static class SimplePlainAuth implements Auth
{
private final Properties passwords = new Properties(); // PLAIN passwords, if loaded
private File passwordsFile;
private long passwordsModified;
@Override
public boolean configure(ZMsg msg, boolean verbose)
{
assert (msg.size() == 2);
// For now we don't do anything with domains
@SuppressWarnings("unused")
String domain = msg.popString();
// Get password file and load into HashMap
// If the file doesn't exist we'll get an empty map
String filename = msg.popString();
passwordsFile = new File(filename);
if (verbose) {
System.out.printf(
"ZAuth: activated plain-mechanism with password-file: %s%n",
passwordsFile.getAbsolutePath());
}
loadPasswords(true);
return true;
}
@Override
public boolean authorize(ZapRequest request, boolean verbose)
{
// assert (request.username != null);
// Refresh the passwords map if the file changed
loadPasswords(false);
String password = passwords.getProperty(request.username);
if (password != null && password.equals(request.password)) {
if (verbose) {
System.out.printf("ZAuth: Allowed (PLAIN) username=%s\n", request.username);
}
request.userId = request.username;
return true;
}
else {
if (verbose) {
System.out.printf("ZAuth: Denied (PLAIN) username=%s\n", request.username);
}
return false;
}
}
private void loadPasswords(boolean initial)
{
if (!initial) {
final long lastModified = passwordsFile.lastModified();
final long age = System.currentTimeMillis() - lastModified;
if (lastModified > passwordsModified && age > 1000) {
// File has been modified and is stable, clear map
passwords.clear();
}
else {
return;
}
}
passwordsModified = passwordsFile.lastModified();
try (Reader br = new BufferedReader(new FileReader(passwordsFile))) {
passwords.load(br);
}
catch (IOException | IllegalArgumentException ex) {
// Ignore the exception, just don't read the file
}
}
}
public static class SimpleCurveAuth implements Auth
{
private final ZCertStore.Fingerprinter fingerprinter;
private ZCertStore certStore = null;
private boolean allowAny;
public SimpleCurveAuth()
{
this(new ZCertStore.Hasher());
}
public SimpleCurveAuth(ZCertStore.Fingerprinter fingerprinter)
{
this.fingerprinter = fingerprinter;
}
@Override
public boolean configure(ZMsg configuration, boolean verbose)
{
// If location is CURVE_ALLOW_ANY, allow all clients. Otherwise
// treat location as a directory that holds the certificates.
final String location = configuration.popString();
allowAny = location.equals(CURVE_ALLOW_ANY);
if (allowAny) {
if (verbose) {
System.out.println("ZAuth: Allowing all clients");
}
}
else {
if (verbose) {
System.out.printf("ZAuth: Using %s as certificates directory%n", location);
}
certStore = new ZCertStore(location, fingerprinter);
}
return true;
}
@Override
public boolean authorize(ZapRequest request, boolean verbose)
{
if (allowAny) {
if (verbose) {
System.out.println("ZAuth: allowed (CURVE allow any client)");
}
return true;
}
else {
if (certStore != null) {
if (certStore.containsPublicKey(request.clientKey)) {
// login allowed
if (verbose) {
System.out.printf("ZAuth: Allowed (CURVE) client_key=%s\n", request.clientKey);
}
request.userId = request.clientKey;
request.metadata = certStore.getMetadata(request.clientKey);
return true;
}
else {
// login not allowed. couldn't find certificate
if (verbose) {
System.out.printf("ZAuth: Denied (CURVE) client_key=%s\n", request.clientKey);
}
return false;
}
}
}
return false;
}
}
public static class SimpleNullAuth implements Auth
{
@Override
public boolean configure(ZMsg configuration, boolean verbose)
{
return true;
}
@Override
public boolean authorize(ZapRequest request, boolean verbose)
{
return true;
}
}
private static final String ZAP_VERSION = "1.0";
public static class ZapReply
{
public final String version; // Version number, must be "1.0"
public final String sequence; // Sequence number of request
public final int statusCode; // numeric status code
public final String statusText; // readable status
public final String userId; // User-Id
public final ZMetadata metadata; // optional metadata
public final String address; // not part of the ZAP protocol, but handy information for user
public final String identity; // not part of the ZAP protocol, but handy information for user
private ZapReply(String version, String sequence, int statusCode, String statusText, String userId,
ZMetadata metadata)
{
this(version, sequence, statusCode, statusText, userId, metadata, null, null);
}
private ZapReply(String version, String sequence, int statusCode, String statusText, String userId,
ZMetadata metadata, String address, String identity)
{
assert (ZAP_VERSION.equals(version));
this.version = version;
this.sequence = sequence;
this.statusCode = statusCode;
this.statusText = statusText;
this.userId = userId;
this.metadata = metadata;
this.address = address;
this.identity = identity;
}
private ZMsg msg()
{
ZMsg msg = new ZMsg();
msg.add(version);
msg.add(sequence);
msg.add(Integer.toString(statusCode));
msg.add(statusText);
msg.add(userId == null ? "" : userId);
msg.add(metadata == null ? new byte[0] : metadata.bytes());
return msg;
}
@Override
public String toString()
{
return "ZapReply [" + (version != null ? "version=" + version + ", " : "")
+ (sequence != null ? "sequence=" + sequence + ", " : "") + "statusCode=" + statusCode + ", "
+ (statusText != null ? "statusText=" + statusText + ", " : "")
+ (userId != null ? "userId=" + userId + ", " : "")
+ (metadata != null ? "metadata=" + metadata : "") + "]";
}
private static ZapReply recv(ZAgent agent, boolean wait)
{
return received(agent.recv(wait));
}
private static ZapReply recv(ZAgent agent, int timeout)
{
return received(agent.recv(timeout));
}
private static ZapReply received(ZMsg msg)
{
if (msg == null) {
return null;
}
assert (msg.size() == 8);
String version = msg.popString();
String sequence = msg.popString();
int statusCode = Integer.parseInt(msg.popString());
String statusText = msg.popString();
String userId = msg.popString();
ZMetadata metadata = ZMetadata.read(msg.popString());
String address = msg.popString();
String identity = msg.popString();
return new ZapReply(version, sequence, statusCode, statusText, userId, metadata, address, identity);
}
}
/**
* A small class for working with ZAP requests and replies.
*/
public static class ZapRequest
{
private final Socket handler; // socket we're talking to
public final String version; // Version number, must be "1.0"
public final String sequence; // Sequence number of request
public final String domain; // Server socket domain
public final String address; // Client IP address
public final String identity; // Server socket identity
public final String mechanism; // Security mechanism
public final String username; // PLAIN user name
public final String password; // PLAIN password, in clear text
public final String clientKey; // CURVE client public key in ASCII
public final String principal; // GSSAPI principal
public String userId; // User-Id to return in the ZAP Response
public ZMetadata metadata; // metadata to eventually return
private ZapRequest(Socket handler, ZMsg request)
{
// Store handler socket so we can send a reply easily
this.handler = handler;
// Get all standard frames off the handler socket
version = request.popString();
sequence = request.popString();
domain = request.popString();
address = request.popString();
identity = request.popString();
mechanism = request.popString();
// If the version is wrong, we're linked with a bogus libzmq, so die
assert (ZAP_VERSION.equals(version));
// Get mechanism-specific frames
if (Mechanism.PLAIN.name().equals(mechanism)) {
username = request.popString();
password = request.popString();
clientKey = null;
principal = null;
}
else if (Mechanism.CURVE.name().equals(mechanism)) {
ZFrame frame = request.pop();
byte[] clientPublicKey = frame.getData();
username = null;
password = null;
clientKey = ZMQ.Curve.z85Encode(clientPublicKey);
principal = null;
}
else if (zmq.io.mechanism.Mechanisms.GSSAPI.name().equals(mechanism)) {
// TOD handle GSSAPI as well
username = null;
password = null;
clientKey = null;
principal = request.popString();
}
else {
username = null;
password = null;
clientKey = null;
principal = null;
}
}
private static ZapRequest recvRequest(Socket handler, boolean wait)
{
ZMsg request = ZMsg.recvMsg(handler, wait);
if (request == null) {
return null;
}
ZapRequest self = new ZapRequest(handler, request);
// If the version is wrong, we're linked with a bogus libzmq, so die
assert (ZAP_VERSION.equals(self.version));
request.destroy();
return self;
}
/**
* Send a zap reply to the handler socket
*/
private void reply(int statusCode, String statusText, Socket replies)
{
ZapReply reply = new ZapReply(ZAP_VERSION, sequence, statusCode, statusText, userId, metadata);
ZMsg msg = reply.msg();
boolean destroy = replies == null;
msg.send(handler, destroy);
if (replies != null) {
// let's add other fields for convenience of listener
msg.add(address);
msg.add(identity);
msg.send(replies);
}
}
}
public static final String CURVE_ALLOW_ANY = "*";
private static final String VERBOSE = "VERBOSE";
private static final String REPLIES = "REPLIES";
private static final String ALLOW = "ALLOW";
private static final String DENY = "DENY";
private static final String TERMINATE = "TERMINATE";
private final ZAgent agent;
private final ZStar.Exit exit;
private final ZAgent replies;
private boolean repliesEnabled; // are replies enabled?
/**
* Install authentication for the specified context. Note that until you add
* policies, all incoming NULL connections are allowed (classic ZeroMQ
* behavior), and all PLAIN and CURVE connections are denied.
* @param ctx
*/
public ZAuth(ZContext ctx)
{
this(ctx, "ZAuth");
}
public ZAuth(ZContext ctx, ZCertStore.Fingerprinter fingerprinter)
{
this(ctx, "ZAuth", curveVariant(fingerprinter));
}
public ZAuth(ZContext ctx, String actorName)
{
this(ctx, actorName, makeSimpleAuths());
}
private static Map makeSimpleAuths()
{
Map auths = new HashMap<>();
auths.put(Mechanism.PLAIN.name(), new SimplePlainAuth());
auths.put(Mechanism.CURVE.name(), new SimpleCurveAuth());
auths.put(Mechanism.NULL.name(), new SimpleNullAuth());
// TODO add GSSAPI once it is implemented
return auths;
}
private static Map curveVariant(ZCertStore.Fingerprinter fingerprinter)
{
Map auths = makeSimpleAuths();
auths.put(Mechanism.CURVE.name(), new SimpleCurveAuth(fingerprinter));
return auths;
}
public ZAuth(final ZContext ctx, String actorName, Map auths)
{
Objects.requireNonNull(ctx, "ZAuth works only with a provided ZContext");
Objects.requireNonNull(actorName, "Actor name shall be defined");
Objects.requireNonNull(auths, "Authenticators shall be supplied as non-null map");
final AuthActor actor = new AuthActor(actorName, auths);
final ZActor zactor = new ZActor(ctx, actor, UUID.randomUUID().toString());
agent = zactor.agent();
exit = zactor.exit();
// wait for the start of the actor
agent.recv().destroy();
replies = actor.createAgent(ctx);
}
/**
* Enable verbose tracing of commands and activity
* @param verbose
*/
public ZAuth setVerbose(boolean verbose)
{
return verbose(verbose);
}
public ZAuth verbose(boolean verbose)
{
return send(VERBOSE, String.format("%b", verbose));
}
/**
* Allow (whitelist) a single IP address. For NULL, all clients from this
* address will be accepted. For PLAIN and CURVE, they will be allowed to
* continue with authentication. You can call this method multiple times to
* whitelist multiple IP addresses. If you whitelist a single address, any
* non-whitelisted addresses are treated as blacklisted.
* @param address
*/
public ZAuth allow(String address)
{
Objects.requireNonNull(address, "Address has to be supplied for allowance");
return send(ALLOW, address);
}
/**
* Deny (blacklist) a single IP address. For all security mechanisms, this
* rejects the connection without any further authentication. Use either a
* whitelist, or a blacklist, not not both. If you define both a whitelist
* and a blacklist, only the whitelist takes effect.
* @param address
*/
public ZAuth deny(String address)
{
Objects.requireNonNull(address, "Address has to be supplied for denial");
return send(DENY, address);
}
/**
* Configure PLAIN authentication for a given domain. PLAIN authentication
* uses a plain-text password file. To cover all domains, use "*". You can
* modify the password file at any time; it is reloaded automatically.
* @param domain
* @param filename
*/
public ZAuth configurePlain(String domain, String filename)
{
Objects.requireNonNull(domain, "Domain has to be supplied");
Objects.requireNonNull(filename, "File name has to be supplied");
return send(Mechanism.PLAIN.name(), domain, filename);
}
/**
* Configure CURVE authentication
*
* @param location Can be ZAuth.CURVE_ALLOW_ANY or a directory with public-keys that will be accepted
*/
public ZAuth configureCurve(String location)
{
Objects.requireNonNull(location, "Location has to be supplied");
return send(Mechanism.CURVE.name(), location);
}
public ZAuth replies(boolean enable)
{
repliesEnabled = enable;
return send(REPLIES, String.format("%b", enable));
}
/**
* Retrieves the next ZAP reply.
* @return the next reply or null if the actor is closed.
*/
public ZapReply nextReply()
{
return nextReply(true);
}
/**
* Retrieves the next ZAP reply.
* @param wait true to wait for the next reply, false to immediately return if there is no next reply.
* @return the next reply or null if the actor is closed or if there is no next reply yet.
*/
public ZapReply nextReply(boolean wait)
{
if (!repliesEnabled) {
System.out.println("ZAuth: replies are disabled. Please use replies(true);");
return null;
}
return ZapReply.recv(replies, wait);
}
/**
* Retrieves the next ZAP reply.
* @param timeout the timeout in milliseconds to wait for a reply before giving up and returning null.
* @return the next reply or null if the actor is closed or if there is no next reply after the elapsed timeout.
*/
public ZapReply nextReply(int timeout)
{
if (!repliesEnabled) {
System.out.println("ZAuth: replies are disabled. Please use replies(true);");
return null;
}
return ZapReply.recv(replies, timeout);
}
/**
* Destructor.
*/
@Override
public void close()
{
destroy();
}
/**
* Destructor.
*/
public void destroy()
{
send(TERMINATE);
exit.awaitSilent();
agent.close();
replies.close();
}
protected ZAuth send(String command, String... datas)
{
ZMsg msg = new ZMsg();
msg.add(command);
for (String data : datas) {
msg.add(data);
}
agent.send(msg);
msg.destroy();
agent.recv();
return this;
}
/**
* AuthActor is the backend actor which we talk to over a pipe. This lets
* the actor do work asynchronously in the background while our application
* does other things. This is invisible to the caller, who sees a classic
* API.
*/
private static class AuthActor extends ZActor.SimpleActor
{
private static final String OK = "OK";
private final String actorName;
private final Properties whitelist = new Properties(); // whitelisted addresses
private final Properties blacklist = new Properties(); // blacklisted addresses
private final Map auths = new HashMap<>();
private final String repliesAddress; // address of replies pipe AND safeguard lock for connected agent
private boolean repliesEnabled; // are replies enabled?
private Socket replies; // replies pipe
private boolean verbose; // trace behavior
private AuthActor(String actorName, Map auths)
{
assert (auths != null);
assert (actorName != null);
this.actorName = actorName;
this.auths.putAll(auths);
this.repliesAddress = "inproc://zauth-replies-" + UUID.randomUUID();
}
private ZAgent createAgent(ZContext ctx)
{
Socket pipe = ctx.createSocket(SocketType.PAIR);
boolean rc = pipe.connect(repliesAddress);
assert (rc);
return new ZAgent.SimpleAgent(pipe, repliesAddress);
}
@Override
public String premiere(Socket pipe)
{
return actorName;
}
@Override
public List createSockets(ZContext ctx, Object... args)
{
//create replies pipe that will forward replies to user
replies = ctx.createSocket(SocketType.PAIR);
assert (replies != null);
//create ZAP handler and get ready for requests
Socket handler = ctx.createSocket(SocketType.REP);
assert (handler != null);
return Arrays.asList(handler, replies);
}
@Override
public void start(Socket pipe, List sockets, ZPoller poller)
{
boolean rc;
try {
rc = replies.bind(repliesAddress);
assert (rc);
Socket handler = sockets.get(0);
rc = handler.bind("inproc://zeromq.zap.01");
assert (rc);
rc = poller.register(handler, ZPoller.POLLIN);
assert (rc);
rc = pipe.send(OK);
assert (rc);
}
catch (ZMQException e) {
System.out.println("ZAuth: Error");
e.printStackTrace();
rc = pipe.send("ERROR");
assert (rc);
}
}
@Override
public boolean backstage(Socket pipe, ZPoller poller, int events)
{
ZMsg msg = ZMsg.recvMsg(pipe);
String command = msg.popString();
if (command == null) {
System.out.printf("ZAuth: Closing auth: No command%n");
return false; //interrupted
}
boolean rc;
if (ALLOW.equals(command)) {
String address = msg.popString();
if (verbose) {
System.out.printf("ZAuth: Whitelisting IP address=%s\n", address);
}
whitelist.put(address, OK);
rc = pipe.send(OK);
}
else if (DENY.equals(command)) {
String address = msg.popString();
if (verbose) {
System.out.printf("ZAuth: Blacklisting IP address=%s\n", address);
}
blacklist.put(address, OK);
rc = pipe.send(OK);
}
else if (VERBOSE.equals(command)) {
String verboseStr = msg.popString();
this.verbose = Boolean.parseBoolean(verboseStr);
rc = pipe.send(OK);
}
else if (REPLIES.equals(command)) {
repliesEnabled = Boolean.parseBoolean(msg.popString());
if (verbose) {
if (repliesEnabled) {
System.out.println("ZAuth: Enabled replies");
}
else {
System.out.println("ZAuth: Disabled replies");
}
}
rc = pipe.send(OK);
}
else if (TERMINATE.equals(command)) {
if (repliesEnabled) {
replies.send(repliesAddress); // lock replies agent
}
if (verbose) {
System.out.println("ZAuth: Terminated");
}
pipe.send(OK);
return false;
}
else {
final Auth authenticator = auths.get(command);
if (authenticator != null) {
if (authenticator.configure(msg, verbose)) {
rc = pipe.send(OK);
}
else {
rc = pipe.send("ERROR");
}
}
else {
System.out.printf("ZAuth: Invalid command %s%n", command);
rc = true;
}
}
msg.destroy();
if (!rc) {
System.out.printf("ZAuth: Command in error %s%n", command);
}
return rc;
}
@Override
public boolean stage(Socket socket, Socket pipe, ZPoller poller, int events)
{
ZapRequest request = ZapRequest.recvRequest(socket, true);
if (request == null) {
return false;
}
//is the address explicitly whitelisted or blacklisted?
boolean allowed = false;
boolean denied = false;
if (!whitelist.isEmpty()) {
if (whitelist.containsKey(request.address)) {
allowed = true;
if (verbose) {
System.out.printf("ZAuth: Passed (whitelist) address = %s\n", request.address);
}
}
else {
denied = true;
if (verbose) {
System.out.printf("ZAuth: Denied (not in whitelist) address = %s\n", request.address);
}
}
}
else if (!blacklist.isEmpty()) {
if (blacklist.containsKey(request.address)) {
denied = true;
if (verbose) {
System.out.printf("ZAuth: Denied (blacklist) address = %s\n", request.address);
}
}
else {
allowed = true;
if (verbose) {
System.out.printf("ZAuth: Passed (not in blacklist) address = %s\n", request.address);
}
}
}
//mechanism specific check
if (!denied) {
final Auth auth = auths.get(request.mechanism);
if (auth == null) {
System.out.printf("ZAuth E: Skipping unhandled mechanism %s%n", request.mechanism);
return false;
}
else {
allowed = auth.authorize(request, verbose);
}
}
final Socket reply = repliesEnabled ? replies : null;
if (allowed) {
request.reply(200, OK, reply);
}
else {
request.metadata = null;
request.reply(400, "NO ACCESS", reply);
}
return true;
}
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZBeacon.java 0000664 0000000 0000000 00000041272 14557711263 0021757 0 ustar 00root root 0000000 0000000 package org.zeromq;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.DatagramChannel;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import zmq.util.Objects;
/**
* This class implements a peer-to-peer discovery service for local networks.
* A beacon can broadcast and/or capture service announcements using UDP messages
* on the local area network. This implementation uses IPv4 UDP broadcasts. You can
* define the format of your outgoing beacons, and set a filter that validates incoming
* beacons. Beacons are sent and received asynchronously in the background.
*
*/
public class ZBeacon
{
public static final long DEFAULT_BROADCAST_INTERVAL = 1000L;
public static final String DEFAULT_BROADCAST_HOST = "255.255.255.255"; // is this the source/interface address? or the broadcast address
private static final InetAddress DEFAULT_BROADCAST_HOST_ADDRESS;
private static final InetAddress DEFAULT_BROADCAST_ADDRESS;
static {
try {
DEFAULT_BROADCAST_HOST_ADDRESS = InetAddress.getByName(DEFAULT_BROADCAST_HOST);
DEFAULT_BROADCAST_ADDRESS = InetAddress.getByName("0.0.0.0");
}
catch (UnknownHostException e) {
throw new IllegalArgumentException("Invalid default broadcast address", e);
}
}
private final BroadcastClient broadcastClient;
private final BroadcastServer broadcastServer;
private final AtomicReference prefix = new AtomicReference<>();
private final AtomicReference beacon = new AtomicReference<>();
private final AtomicLong broadcastInterval = new AtomicLong(DEFAULT_BROADCAST_INTERVAL);
private final AtomicReference listener = new AtomicReference<>();
private final AtomicReference clientExHandler = new AtomicReference<>();
private final AtomicReference serverExHandler = new AtomicReference<>();
public ZBeacon(int port, byte[] beacon)
{
this(DEFAULT_BROADCAST_HOST, port, beacon);
}
public ZBeacon(String host, int port, byte[] beacon)
{
this(host, port, beacon, true);
}
public ZBeacon(String host, int port, byte[] beacon, boolean ignoreLocalAddress)
{
this(host, port, beacon, ignoreLocalAddress, false);
}
public ZBeacon(String host, int port, byte[] beacon, boolean ignoreLocalAddress, boolean blocking)
{
this(host, DEFAULT_BROADCAST_ADDRESS.getAddress(), port, beacon, DEFAULT_BROADCAST_INTERVAL, ignoreLocalAddress, blocking);
}
public ZBeacon(InetAddress broadcastAddress, InetAddress serverAddress, int port, byte[] beacon, long broadcastInterval, boolean ignoreLocalAddress, boolean blocking)
{
Objects.requireNonNull(broadcastAddress, "Host cannot be null");
Objects.requireNonNull(serverAddress, "Server address cannot be null");
Objects.requireNonNull(beacon, "Beacon cannot be null");
this.broadcastInterval.set(broadcastInterval);
this.beacon.set(Arrays.copyOf(beacon, beacon.length));
broadcastServer = new BroadcastServer(port, ignoreLocalAddress);
broadcastClient = new BroadcastClient(serverAddress, broadcastAddress, port, this.broadcastInterval);
}
@Deprecated
public ZBeacon(String broadcastAddress, byte[] serverAddress, int port, byte[] beacon, long broadcastInterval, boolean ignoreLocalAddress, boolean blocking)
{
Objects.requireNonNull(broadcastAddress, "Host cannot be null");
Objects.requireNonNull(serverAddress, "Server address cannot be null");
Objects.requireNonNull(beacon, "Beacon cannot be null");
this.broadcastInterval.set(broadcastInterval);
this.beacon.set(Arrays.copyOf(beacon, beacon.length));
broadcastServer = new BroadcastServer(port, ignoreLocalAddress);
try {
broadcastClient = new BroadcastClient(InetAddress.getByAddress(serverAddress), InetAddress.getByName(broadcastAddress), port, this.broadcastInterval);
}
catch (UnknownHostException e) {
throw new IllegalArgumentException("Invalid server address", e);
}
}
public static class Builder
{
private InetAddress clientHost = DEFAULT_BROADCAST_HOST_ADDRESS;
private InetAddress serverAddr = DEFAULT_BROADCAST_ADDRESS;
private int port;
private long broadcastInterval = DEFAULT_BROADCAST_INTERVAL;
private byte[] beacon;
private boolean ignoreLocalAddress = true;
private boolean blocking = false;
// Those arguments are not set using the constructor, so they are optional for backward compatibility
private Listener listener = null;
private byte[] prefix = null;
private Thread.UncaughtExceptionHandler clientExHandler = null;
private Thread.UncaughtExceptionHandler serverExHandler = null;
public Builder port(int port)
{
this.port = port;
return this;
}
public Builder beacon(byte[] beacon)
{
this.beacon = beacon;
return this;
}
@Deprecated
public Builder client(String host)
{
try {
this.clientHost = InetAddress.getByName(host);
}
catch (UnknownHostException e) {
throw new IllegalArgumentException("Invalid server address", e);
}
return this;
}
public Builder client(InetAddress host)
{
this.clientHost = host;
return this;
}
@Deprecated
public Builder server(byte[] addr)
{
Utils.checkArgument(addr.length == 4 || addr.length == 16, "Server Address has to be 4 or 16 bytes long");
try {
this.serverAddr = InetAddress.getByAddress(addr);
}
catch (UnknownHostException e) {
throw new IllegalArgumentException("Invalid server address", e);
}
return this;
}
public Builder server(InetAddress addr)
{
this.serverAddr = addr;
return this;
}
public Builder ignoreLocalAddress(boolean ignoreLocalAddress)
{
this.ignoreLocalAddress = ignoreLocalAddress;
return this;
}
/**
* @deprecated ignored
* @param blocking
* @return
*/
@Deprecated
public Builder blocking(boolean blocking)
{
this.blocking = blocking;
return this;
}
public Builder broadcastInterval(long broadcastInterval)
{
this.broadcastInterval = broadcastInterval;
return this;
}
public Builder listener(Listener listener)
{
this.listener = listener;
return this;
}
public Builder prefix(byte[] prefix)
{
this.prefix = Arrays.copyOf(prefix, prefix.length);
return this;
}
public Builder setClientUncaughtExceptionHandlers(Thread.UncaughtExceptionHandler clientExHandler)
{
this.clientExHandler = clientExHandler;
return this;
}
public Builder setServerUncaughtExceptionHandlers(Thread.UncaughtExceptionHandler serverExHandler)
{
this.serverExHandler = serverExHandler;
return this;
}
public ZBeacon build()
{
ZBeacon zbeacon = new ZBeacon(clientHost, serverAddr, port, beacon, broadcastInterval, ignoreLocalAddress, blocking);
if (listener != null) {
zbeacon.setListener(listener);
}
if (prefix != null) {
zbeacon.setPrefix(prefix);
}
if (this.serverExHandler != null) {
zbeacon.serverExHandler.set(this.serverExHandler);
}
if (this.clientExHandler != null) {
zbeacon.clientExHandler.set(this.clientExHandler);
}
return zbeacon;
}
}
/**
* @deprecated use the builder
* @param clientExHandler
* @param serverExHandler
*/
@Deprecated
public void setUncaughtExceptionHandlers(Thread.UncaughtExceptionHandler clientExHandler,
Thread.UncaughtExceptionHandler serverExHandler)
{
this.clientExHandler.set(clientExHandler);
this.serverExHandler.set(serverExHandler);
}
public void startClient()
{
if (!broadcastClient.isRunning) {
if (broadcastClient.thread == null) {
broadcastClient.thread = new Thread(broadcastClient);
broadcastClient.thread.setName("ZBeacon Client Thread");
broadcastClient.thread.setDaemon(true);
broadcastClient.thread.setUncaughtExceptionHandler(clientExHandler.get());
}
broadcastClient.thread.start();
}
}
public void startServer()
{
if (!broadcastServer.isRunning) {
if (listener.get() != null) {
if (broadcastServer.thread == null) {
broadcastServer.thread = new Thread(broadcastServer);
broadcastServer.thread.setName("ZBeacon Server Thread");
broadcastServer.thread.setDaemon(true);
broadcastServer.thread.setUncaughtExceptionHandler(serverExHandler.get());
}
broadcastServer.thread.start();
}
}
}
public void start()
{
startClient();
startServer();
}
public void stop() throws InterruptedException
{
if (broadcastClient.thread != null) {
broadcastClient.thread.interrupt();
broadcastClient.thread.join();
}
if (broadcastServer.thread != null) {
broadcastServer.thread.interrupt();
broadcastServer.thread.join();
}
}
/**
* @deprecated use the builder
* @param beacon
*/
@Deprecated
public void setBeacon(byte[] beacon)
{
this.beacon.set(Arrays.copyOf(beacon, beacon.length));
}
public byte[] getBeacon()
{
byte[] beaconBuffer = beacon.get();
return Arrays.copyOf(beaconBuffer, beaconBuffer.length);
}
/**
* @deprecated use the builder
* @param prefix
*/
@Deprecated
public void setPrefix(byte[] prefix)
{
this.prefix.set(Arrays.copyOf(prefix, prefix.length));
}
public byte[] getPrefix()
{
byte[] prefixBuffer = prefix.get();
return Arrays.copyOf(prefixBuffer, prefixBuffer.length);
}
/**
* @deprecated use the builder
* @param listener
*/
@Deprecated
public void setListener(Listener listener)
{
this.listener.set(listener);
}
public Listener getListener()
{
return listener.get();
}
/**
* All beacons with matching prefix are passed to a listener.
*/
public interface Listener
{
void onBeacon(InetAddress sender, byte[] beacon);
}
/**
* The broadcast client periodically sends beacons via UDP to the network.
*/
private class BroadcastClient implements Runnable
{
private final InetSocketAddress broadcastAddress;
private final InetAddress interfaceAddress;
private final AtomicLong broadcastInterval;
private boolean isRunning;
private Thread thread;
public BroadcastClient(InetAddress interfaceAddress, InetAddress broadcastAddress, int port, AtomicLong broadcastInterval)
{
this.broadcastInterval = broadcastInterval;
this.broadcastAddress = new InetSocketAddress(broadcastAddress, port);
this.interfaceAddress = interfaceAddress;
}
@Override
public void run()
{
try (DatagramChannel broadcastChannel = DatagramChannel.open()) {
broadcastChannel.socket().setBroadcast(true);
broadcastChannel.socket().setReuseAddress(true);
broadcastChannel.socket().bind(new InetSocketAddress(interfaceAddress, 0));
broadcastChannel.connect(broadcastAddress);
isRunning = true;
while (!Thread.interrupted() && isRunning) {
try {
broadcastChannel.send(ByteBuffer.wrap(beacon.get()), broadcastAddress);
Thread.sleep(broadcastInterval.get());
}
catch (InterruptedException | ClosedByInterruptException interruptedException) {
// Re-interrupt the thread so the caller can handle it.
Thread.currentThread().interrupt();
break;
}
catch (Exception exception) {
throw new RuntimeException(exception);
}
}
}
catch (IOException ioException) {
throw new RuntimeException(ioException);
}
finally {
isRunning = false;
thread = null;
}
}
}
/**
* The broadcast server receives beacons.
*/
private class BroadcastServer implements Runnable
{
private final DatagramChannel handle; // Socket for send/recv
private final boolean ignoreLocalAddress;
private Thread thread;
private boolean isRunning;
public BroadcastServer(int port, boolean ignoreLocalAddress)
{
this.ignoreLocalAddress = ignoreLocalAddress;
try {
// Create UDP socket
handle = DatagramChannel.open();
handle.configureBlocking(true);
handle.socket().setReuseAddress(true);
handle.socket().bind(new InetSocketAddress(port));
}
catch (IOException ioException) {
throw new RuntimeException(ioException);
}
}
@Override
public void run()
{
ByteBuffer buffer = ByteBuffer.allocate(65535);
SocketAddress sender;
isRunning = true;
try {
while (!Thread.interrupted() && isRunning) {
buffer.clear();
try {
sender = handle.receive(buffer);
InetAddress senderAddress = ((InetSocketAddress) sender).getAddress();
if (ignoreLocalAddress
&& (InetAddress.getLocalHost().getHostAddress().equals(senderAddress.getHostAddress())
|| senderAddress.isAnyLocalAddress() || senderAddress.isLoopbackAddress())) {
continue;
}
handleMessage(buffer, senderAddress);
}
catch (ClosedChannelException ioException) {
break;
}
catch (IOException ioException) {
isRunning = false;
throw new RuntimeException(ioException);
}
}
}
finally {
handle.socket().close();
isRunning = false;
thread = null;
}
}
private void handleMessage(ByteBuffer buffer, InetAddress from)
{
byte[] prefix = ZBeacon.this.prefix.get();
if (buffer.remaining() < prefix.length) {
return;
}
buffer.flip();
buffer.mark();
byte[] prefixTry = new byte[prefix.length];
buffer.get(prefixTry);
if (Arrays.equals(prefix, prefixTry)) {
buffer.reset();
byte[] content = new byte[buffer.remaining()];
buffer.get(content);
listener.get().onBeacon(from, content);
}
}
}
public long getBroadcastInterval()
{
return broadcastInterval.get();
}
public void setBroadcastInterval(long broadcastInterval)
{
this.broadcastInterval.set(broadcastInterval);
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZCert.java 0000664 0000000 0000000 00000016062 14557711263 0021464 0 ustar 00root root 0000000 0000000 package org.zeromq;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import org.zeromq.ZMQ.Curve;
import org.zeromq.ZMQ.Curve.KeyPair;
import org.zeromq.util.ZMetadata;
/**
*
The ZCert class provides a way to create and work with security
certificates for the ZMQ CURVE mechanism. A certificate contains a
public + secret key pair, plus metadata. It can be used as a
temporary object in memory, or persisted to disk.
To exchange certificates, send the public file via some secure route.
Certificates are not signed but are text files that can be verified by
eye.
Certificates are stored in the ZeroMQ Property Language format .
They have two sections, "metadata" and "curve".
The first contains a list of 'name = value' pairs, one per line.
Values may be enclosed in quotes.
The curve section has a 'public-key = key-value' and, for secret certificates, a
'secret-key = key-value' line.
The key-value is a {@link zmq.util.Z85 Z85-encoded CURVE key}.
*
*/
public class ZCert
{
private final byte[] publicKey; // Public key in binary
private final byte[] secretKey; // Secret key in binary
private final String publicTxt; // Public key in Z85 text
private final String secretTxt; // Secret key in Z85 text
private final ZMetadata metadata = new ZMetadata(); // Certificate metadata
public ZCert()
{
this(ZMQ.Curve.generateKeyPair());
}
public ZCert(String publicKey)
{
this(publicKey, null);
}
public ZCert(KeyPair keypair)
{
this(keypair.publicKey, keypair.secretKey);
}
public ZCert(byte[] publicKey, byte[] secretKey)
{
Utils.checkArgument(publicKey != null, "Public key has to be provided for a ZCert");
assertKey(publicKey.length, Curve.KEY_SIZE, "Public");
if (secretKey != null) {
assertKey(secretKey.length, Curve.KEY_SIZE, "Secret");
}
this.publicKey = Arrays.copyOf(publicKey, publicKey.length);
this.publicTxt = Curve.z85Encode(this.publicKey);
if (secretKey == null) {
this.secretKey = null;
this.secretTxt = null;
}
else {
this.secretKey = Arrays.copyOf(secretKey, secretKey.length);
this.secretTxt = Curve.z85Encode(this.secretKey);
}
}
public ZCert(String publicKey, String secretKey)
{
Utils.checkArgument(publicKey != null, "Public key has to be provided for a ZCert");
assertKey(publicKey.length(), Curve.KEY_SIZE_Z85, "Public");
if (secretKey != null) {
assertKey(secretKey.length(), Curve.KEY_SIZE_Z85, "Secret");
}
this.publicKey = Curve.z85Decode(publicKey);
this.publicTxt = publicKey;
if (secretKey == null) {
this.secretKey = null;
this.secretTxt = null;
}
else {
this.secretKey = Curve.z85Decode(secretKey);
this.secretTxt = secretKey;
}
}
private void assertKey(int length, int expected, String flavour)
{
Utils.checkArgument(length == expected, flavour + " key shall have a size of " + expected);
}
public byte[] getPublicKey()
{
return publicKey;
}
public byte[] getSecretKey()
{
return secretKey;
}
public String getPublicKeyAsZ85()
{
return publicTxt;
}
public String getSecretKeyAsZ85()
{
return secretTxt;
}
public void apply(ZMQ.Socket socket)
{
socket.setCurvePublicKey(publicKey);
socket.setCurveSecretKey(secretKey);
}
public ZMetadata getMetadata()
{
return metadata;
}
public void setMeta(String key, String value)
{
metadata.set(key, value);
}
public void unsetMeta(String key)
{
metadata.remove(key);
}
public String getMeta(String key)
{
return metadata.get(key);
}
private void add(ZMetadata meta, ZConfig config)
{
String now = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.ENGLISH).format(new Date());
config.addComment(String.format("** Generated on %1$s by ZCert **", now));
for (Map.Entry e : meta.entrySet()) {
config.putValue("metadata/" + e.getKey(), e.getValue());
}
}
/**
* Saves the public key to a file.
*
* This method will overwrite contents of existing file
* @param filename the path of the file to save the certificate into.
* @return the saved file or null if dumped to the standard output
* @throws IOException if unable to save the file.
*/
public File savePublic(String filename) throws IOException
{
return publicConfig().save(filename);
}
/**
* Saves the public key to a writer.
* @param writer the writer to save the certificate into.
* @throws IOException if unable to dump the public configuration.
*/
public void savePublic(Writer writer) throws IOException
{
publicConfig().save(writer);
}
private ZConfig publicConfig()
{
ZConfig conf = new ZConfig("root", null);
add(metadata, conf);
conf.addComment(" ZeroMQ CURVE Public Certificate");
conf.addComment(" Exchange securely, or use a secure mechanism to verify the contents");
conf.addComment(" of this file after exchange. Store public certificates in your home");
conf.addComment(" directory, in the .curve subdirectory.");
conf.putValue("/curve/public-key", publicTxt);
return conf;
}
/**
* Saves the public and secret keys to a file.
*
* This method will overwrite contents of existing file
* @param filename the path of the file to save the certificate into.
* @return the saved file or null if dumped to the standard output
* @throws IOException if unable to save the file.
*/
public File saveSecret(String filename) throws IOException
{
return secretConfig().save(filename);
}
/**
* Saves the public and secret keys to a writer.
* @param writer the writer to save the certificate into.
* @throws IOException if unable to dump the configuration.
*/
public void saveSecret(Writer writer) throws IOException
{
secretConfig().save(writer);
}
private ZConfig secretConfig()
{
ZConfig conf = new ZConfig("root", null);
add(metadata, conf);
conf.addComment(" ZeroMQ CURVE **Secret** Certificate");
conf.addComment(" DO NOT PROVIDE THIS FILE TO OTHER USERS nor change its permissions.");
conf.putValue("/curve/public-key", publicTxt);
conf.putValue("/curve/secret-key", secretTxt);
return conf;
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZCertStore.java 0000664 0000000 0000000 00000023067 14557711263 0022504 0 ustar 00root root 0000000 0000000 package org.zeromq;
import org.zeromq.util.ZDigest;
import org.zeromq.util.ZMetadata;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
To authenticate new clients using the ZeroMQ CURVE security mechanism,
we have to check that the client's public key matches a key we know and
accept. There are numerous ways to store accepted client public keys.
The mechanism CZMQ implements is "certificates" (plain text files) held
in a "certificate store" (a disk directory). This class works with such
certificate stores, and lets you easily load them from disk, and check
if a given client public key is known or not. The {@link org.zeromq.ZCert} class does the
work of managing a single certificate.
*
Those files need to be in ZMP-Format which is created by {@link org.zeromq.ZConfig}
*/
public class ZCertStore
{
public interface Fingerprinter
{
byte[] print(File path);
}
public static final class Timestamper implements Fingerprinter
{
@Override
public byte[] print(File path)
{
final byte[] buf = new byte[8];
final long value = path.lastModified();
buf[0] = (byte) ((value >>> 56) & 0xff);
buf[1] = (byte) ((value >>> 48) & 0xff);
buf[2] = (byte) ((value >>> 40) & 0xff);
buf[3] = (byte) ((value >>> 32) & 0xff);
buf[4] = (byte) ((value >>> 24) & 0xff);
buf[5] = (byte) ((value >>> 16) & 0xff);
buf[6] = (byte) ((value >>> 8) & 0xff);
buf[7] = (byte) ((value) & 0xff);
return buf;
}
}
public static final class Hasher implements Fingerprinter
{
// temporary buffer used for digest. Instance member for performance reasons.
private final byte[] buffer = new byte[8192];
@Override
public byte[] print(File path)
{
InputStream input = stream(path);
if (input != null) {
try {
return new ZDigest(buffer).update(input).data();
}
catch (IOException e) {
return null;
}
finally {
try {
input.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
private InputStream stream(File path)
{
if (path.isFile()) {
try {
return new FileInputStream(path);
}
catch (FileNotFoundException e) {
return null;
}
}
else if (path.isDirectory()) {
List list = Arrays.asList(path.list());
Collections.sort(list);
return new ByteArrayInputStream(list.toString().getBytes(ZMQ.CHARSET));
}
return null;
}
}
private interface IFileVisitor
{
/**
* Visits a file.
* @param file the file to visit.
* @return true to stop the traversal, false to continue.
*/
boolean visitFile(File file);
/**
* Visits a directory.
* @param dir the directory to visit.
* @return true to stop the traversal, false to continue.
*/
boolean visitDir(File dir);
}
// Directory location
private final File location;
// the scanned files (and directories) along with their fingerprint
private final Map fingerprints = new HashMap<>();
// collected public keys
private final Map publicKeys = new HashMap<>();
private final Fingerprinter finger;
/**
* Create a Certificate Store at that file system folder location
* @param location
*/
public ZCertStore(String location)
{
this(location, new Timestamper());
}
public ZCertStore(String location, Fingerprinter fingerprinter)
{
this.finger = fingerprinter;
this.location = new File(location);
loadFiles();
}
private boolean traverseDirectory(File root, IFileVisitor visitor)
{
assert (root.exists());
assert (root.isDirectory());
if (visitor.visitDir(root)) {
return true;
}
for (File file : root.listFiles()) {
if (file.isFile()) {
if (visitor.visitFile(file)) {
return true;
}
}
else if (file.isDirectory()) {
boolean rc = traverseDirectory(file, visitor);
if (rc) {
return true;
}
}
else {
System.out.printf(
"WARNING: %s is neither file nor directory? This shouldn't happen....SKIPPING%n",
file.getAbsolutePath());
}
}
return false;
}
/**
* Check if a public key is in the certificate store.
* @param publicKey needs to be a 32 byte array representing the public key
*/
public boolean containsPublicKey(byte[] publicKey)
{
Utils.checkArgument(
publicKey.length == 32,
"publickey needs to have a size of 32 bytes. got only " + publicKey.length);
return containsPublicKey(ZMQ.Curve.z85Encode(publicKey));
}
/**
* check if a z85-based public key is in the certificate store.
* This method will scan the folder for changes on every call
* @param publicKey
*/
public boolean containsPublicKey(String publicKey)
{
Utils.checkArgument(
publicKey.length() == 40,
"z85 publickeys should have a length of 40 bytes but got " + publicKey.length());
reloadIfNecessary();
return publicKeys.containsKey(publicKey);
}
public ZMetadata getMetadata(String publicKey)
{
reloadIfNecessary();
return publicKeys.get(publicKey);
}
private void loadFiles()
{
final Map keys = new HashMap<>();
if (!location.exists()) {
location.mkdirs();
}
final Map collected = new HashMap<>();
traverseDirectory(location, new IFileVisitor()
{
@Override
public boolean visitFile(File file)
{
try {
ZConfig zconf = ZConfig.load(file.getAbsolutePath());
String publicKey = zconf.getValue("curve/public-key");
if (publicKey == null) {
System.out.printf(
"Warning!! File %s has no curve/public-key-element. SKIPPING!%n",
file.getAbsolutePath());
return false;
}
if (publicKey.length() == 32) { // we want to store the public-key as Z85-String
publicKey = ZMQ.Curve.z85Encode(publicKey.getBytes(ZMQ.CHARSET));
}
assert (publicKey.length() == 40);
keys.put(publicKey, ZMetadata.read(zconf));
collected.put(file, finger.print(file));
}
catch (IOException e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean visitDir(File dir)
{
collected.put(dir, finger.print(dir));
return false;
}
});
publicKeys.clear();
publicKeys.putAll(keys);
fingerprints.clear();
fingerprints.putAll(collected);
}
int getCertificatesCount()
{
reloadIfNecessary();
return publicKeys.size();
}
boolean reloadIfNecessary()
{
if (checkForChanges()) {
loadFiles();
return true;
}
return false;
}
/**
* Check if files in the certificate folders have been added or removed.
*/
boolean checkForChanges()
{
// initialize with last checked files
final Map presents = new HashMap<>(fingerprints);
boolean modified = traverseDirectory(location, new IFileVisitor()
{
@Override
public boolean visitFile(File file)
{
return modified(presents.remove(file), file);
}
@Override
public boolean visitDir(File dir)
{
return modified(presents.remove(dir), dir);
}
});
// if some files remain, that means they have been deleted since last scan
return modified || !presents.isEmpty();
}
private boolean modified(byte[] fingerprint, File path)
{
if (!path.exists()) {
// run load-files if one file is not present
return true;
}
if (fingerprint == null) {
// file was not scanned before, it has been added
return true;
}
// true if file has been modified.
return !Arrays.equals(fingerprint, finger.print(path));
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZConfig.java 0000664 0000000 0000000 00000026226 14557711263 0021777 0 ustar 00root root 0000000 0000000 package org.zeromq;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Lets applications load, work with, and save configuration files.
* This is a minimal implementation of the ZeroMQ Property Language ,
* which is a simple structured text format for configuration files.
*
* Here is an example ZPL stream and corresponding config structure:
*
*
* {@code
context
iothreads = 1
verbose = 1 # Ask for a trace
main
type = zqueue # ZMQ_DEVICE type
frontend
option
hwm = 1000
swap = 25000000 # 25MB
bind = 'inproc://addr1'
bind = 'ipc://addr2'
backend
bind = inproc://addr3
}
{@code
root Down = child
| Across = next
v
context-->main
| |
| v
| type=queue-->frontend-->backend
| | |
| | v
| | bind=inproc://addr3
| v
| option-->bind=inproc://addr1-->bind=ipc://addr2
| |
| v
| hwm=1000-->swap=25000000
v
iothreads=1-->verbose=false
}
*
* It can put and get values and save and load them to disk:
*
*
* {@code
* ZConfig conf = new ZConfig("root", null);
* conf.put("/curve/public-key","abcdef");
* String val = conf.get("/curve/public-key","fallback-defaultkey");
* conf.save("test.cert");
* ZConfig loaded = ZConfig.load("test.cert");
}
*/
public class ZConfig
{
private interface IVisitor
{
void handleNode(ZConfig node, int level) throws IOException;
}
private static final String LEFT = "^( *)([0-9a-zA-Z\\$\\-_@\\.&\\+\\/]+)";
private static final Pattern PTRN_CONTAINER = Pattern.compile(LEFT + "( *#.*)?$");
private static final Pattern PTRN_KEYVALUE = Pattern.compile(LEFT + " = ((\"|')(.*)(\\4)|(.*?))(#.*)?$");
private final String name;
private final Map children = new HashMap<>();
private final List comments = new LinkedList<>();
private String value;
public ZConfig(String name, ZConfig parent)
{
this.name = name;
if (parent != null) {
parent.children.put(name, this);
}
}
public ZConfig getChild(String name)
{
return children.get(name);
}
public Map getValues()
{
Map values = new HashMap<>();
fillValues("", values);
return values;
}
private void fillValues(String prefix, Map values)
{
for (Entry entry : children.entrySet()) {
String key = entry.getKey();
ZConfig child = entry.getValue();
assert (child != null);
if (child.value != null) {
values.put(prefix + key, child.value);
}
child.fillValues(prefix + key + '/', values);
}
}
public String getName()
{
return this.name;
}
public String getValue(String path)
{
return getValue(path, null);
}
public String getValue(String path, String defaultValue)
{
String[] pathElements = path.split("/");
ZConfig current = this;
for (String pathElem : pathElements) {
if (pathElem.isEmpty()) {
continue;
}
current = current.children.get(pathElem);
if (current == null) {
return defaultValue;
}
}
return current.value;
}
/**
* check if a value-path exists
* @param path
* @return true if value-path exists
*/
public boolean pathExists(String path)
{
String[] pathElements = path.split("/");
ZConfig current = this;
for (String pathElem : pathElements) {
if (pathElem.isEmpty()) {
continue;
}
current = current.children.get(pathElem);
if (current == null) {
return false;
}
}
return true;
}
/**
* add comment
* @param comment
*/
public void addComment(String comment)
{
comments.add(comment);
}
/**
* @param path
* @param value set value of config item
*/
public ZConfig putValue(String path, String value)
{
String[] pathElements = path.split("/");
ZConfig current = this;
for (String pathElement : pathElements) {
if (pathElement.isEmpty()) {
// ignore leading slashes
continue;
}
ZConfig container = current.children.get(pathElement);
if (container == null) {
container = new ZConfig(pathElement, current);
}
current = container;
}
current.value = value;
return current;
}
public void putValues(ZConfig src)
{
for (Entry entry : src.getValues().entrySet()) {
putValue(entry .getKey(), entry.getValue());
}
}
private void visit(ZConfig startNode, IVisitor handler, int level) throws IOException
{
handler.handleNode(startNode, level);
for (ZConfig node : startNode.children.values()) {
visit(node, handler, level + 1);
}
}
/**
* Saves the configuration to a file.
*
* This method will overwrite contents of existing file
* @param filename the path of the file to save the configuration into, or "-" to dump it to standard output
* @return the saved file or null if dumped to the standard output
* @throws IOException if unable to save the file.
*/
public File save(String filename) throws IOException
{
if ("-".equals(filename)) {
// print to console
try (Writer writer = new PrintWriter(System.out)) {
save(writer);
}
return null;
}
else { // write to file
final File file = new File(filename);
if (file.exists()) {
file.delete();
}
else {
// create necessary directories;
file.getParentFile().mkdirs();
}
try (Writer writer = new FileWriter(file)) {
save(writer);
}
return file;
}
}
public void save(final Writer writer) throws IOException
{
visit(this, (node, level) -> {
// First print comments
if (!node.comments.isEmpty()) {
for (String comment : node.comments) {
writer.append("# ").append(comment).append('\n');
}
writer.append("\n");
}
// now the values
if (level > 0) {
String prefix = level > 1 ? String.format("%" + ((level - 1) * 4) + "s", " ") : "";
writer.append(prefix);
if (node.value == null) {
writer.append(node.name).append("\n");
}
else {
writer.append(String.format("%s = \"%s\"\n", node.name, node.value));
}
}
}, 0);
}
public static ZConfig load(String filename) throws IOException
{
try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
List content = new ArrayList<>();
String line = reader.readLine();
while (line != null) {
boolean irrelevant = line.matches("^ *#.*|^ *[0-9]+.*") // ignore comments
|| line.trim().isEmpty(); // ignore empty lines;
if (!irrelevant) {
content.add(line);
}
line = reader.readLine();
}
return load(new ZConfig("root", null), content, 0, new AtomicInteger());
}
}
private static ZConfig load(ZConfig parent, List content, int currentLevel, AtomicInteger lineNumber)
{
while (lineNumber.get() < content.size()) {
String currentLine = content.get(lineNumber.get());
Matcher container = PTRN_CONTAINER.matcher(currentLine);
if (container.find()) {
ZConfig child = child(parent, container, currentLevel, currentLine, lineNumber);
if (child == null) {
// jump back;
break;
}
load(child, content, currentLevel + 1, lineNumber);
}
else {
Matcher keyvalue = PTRN_KEYVALUE.matcher(currentLine);
if (keyvalue.find()) {
ZConfig child = child(parent, keyvalue, currentLevel, currentLine, lineNumber);
if (child == null) {
// jump back;
break;
}
String value = keyvalue.group(5);
if (value == null) {
value = keyvalue.group(7);
}
if (value != null) {
value = value.trim();
}
child.value = value;
}
else {
throw new ReadException("Couldn't process line", currentLine, lineNumber);
}
}
}
return parent;
}
private static ZConfig child(ZConfig parent, Matcher matcher, int currentLevel, String currentLine,
AtomicInteger lineNumber)
{
int level = matcher.group(1).length() / 4;
if (level > currentLevel) {
throw new ReadException("Level mismatch in line", currentLine, lineNumber);
}
else if (level < currentLevel) {
// jump back;
return null;
}
lineNumber.incrementAndGet();
return new ZConfig(matcher.group(2), parent);
}
public static class ReadException extends RuntimeException
{
private static final long serialVersionUID = 1L;
public final int currentLineNumber;
public final String currentLine;
public ReadException(String message, String currentLine, AtomicInteger currentLineNumber)
{
super(String.format("%s %s: %s", message, currentLineNumber, currentLine));
this.currentLine = currentLine;
this.currentLineNumber = currentLineNumber.get();
}
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZContext.java 0000664 0000000 0000000 00000032551 14557711263 0022214 0 ustar 00root root 0000000 0000000 package org.zeromq;
import java.io.Closeable;
import java.lang.Thread.UncaughtExceptionHandler;
import java.nio.channels.Selector;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.zeromq.ZMQ.Context;
import org.zeromq.ZMQ.Poller;
import org.zeromq.ZMQ.Socket;
import zmq.util.Draft;
import zmq.util.function.BiFunction;
/**
* ZContext provides a high-level ZeroMQ context management class
*
* It manages open sockets in the context and automatically closes these before terminating the context.
* It provides a simple way to set the linger timeout on sockets, and configure contexts for number of I/O threads.
* Sets-up signal (interrupt) handling for the process.
*
*/
public class ZContext implements Closeable
{
/**
* Reference to underlying Context object
*/
private final Context context;
/**
* List of sockets managed by this ZContext
*/
private final Set sockets;
/**
* List of selectors managed by this ZContext
*/
private final Set selectors;
/**
* List of ZContext in the shadows
*/
private final Set shadows;
/**
* Number of io threads allocated to this context, default 1
*/
private final int ioThreads;
/**
* Indicates if context object is owned by main thread
* (useful for multi-threaded applications)
*/
private final boolean main;
/**
* Linger timeout, default 0
*/
private volatile int linger;
/**
* Send/receive HWM for pipes
*/
private int pipehwm;
/**
* ZMQ_SNDHWM for normal sockets
*/
private volatile int sndhwm;
/**
* ZMQ_RCVHWM for normal sockets
*/
private volatile int rcvhwm;
/**
* Class Constructor
*/
public ZContext()
{
this(1);
}
public ZContext(int ioThreads)
{
this(null, ioThreads);
}
private ZContext(ZContext parent, int ioThreads)
{
if (parent == null) {
this.main = true;
this.context = ZMQ.context(ioThreads);
this.shadows = Collections.newSetFromMap(new ConcurrentHashMap<>());
}
else {
this.main = false;
this.context = parent.context;
this.shadows = parent.shadows;
this.shadows.add(this);
}
// Android compatibility: not using ConcurrentHashMap.newKeySet()
this.sockets = Collections.newSetFromMap(new ConcurrentHashMap<>());
this.selectors = Collections.newSetFromMap(new ConcurrentHashMap<>());
this.ioThreads = ioThreads;
this.linger = 0;
this.pipehwm = 1000;
this.sndhwm = 1000;
this.rcvhwm = 1000;
}
/**
* Destructor. Call this to gracefully terminate context and close any managed 0MQ sockets
*/
public void destroy()
{
for (Socket socket : sockets) {
socket.internalClose();
}
sockets.clear();
for (Selector selector : selectors) {
context.close(selector);
}
selectors.clear();
// Only terminate context if we are on the main thread
if (isMain()) {
for (ZContext child : shadows) {
child.close();
}
context.term();
}
else {
shadows.remove(this);
}
}
/**
* Creates a new managed socket within this ZContext instance.
* Use this to get automatic management of the socket at shutdown.
*
* The newly created socket will inherited it's linger value from the one
* defined for this context.
* @param type
* socket type
* @return
* Newly created Socket object
*/
public Socket createSocket(SocketType type)
{
// Create and register socket
Socket socket = new Socket(this, type);
socket.setRcvHWM(this.rcvhwm);
socket.setSndHWM(this.sndhwm);
socket.setLinger(this.linger);
sockets.add(socket);
return socket;
}
/**
* @deprecated use {@link #createSocket(SocketType)}
* @param type
* socket type (see ZMQ static class members)
* @return
* Newly created Socket object
*/
@Deprecated
public Socket createSocket(int type)
{
return createSocket(SocketType.type(type));
}
/**
* Destroys a managed socket within this context and remove
* from sockets list. This method should be used only for
* fast or emergency close as is set linger instead of using the
* socket current value.
* @param s {@link org.zeromq.ZMQ.Socket} object to destroy
* @deprecated Not to be used any more. {@link org.zeromq.ZMQ.Socket} handle
* the close itself. It also override linger settings.
*/
@Deprecated
public void destroySocket(Socket s)
{
if (s == null) {
return;
}
s.setLinger(linger);
try {
s.internalClose();
}
finally {
sockets.remove(s);
}
}
/**
* Close managed socket within this context and remove from sockets list.
* There is no need to call this method as any {@link Socket} created by
* this context will call it on termination.
* @param s {@link org.zeromq.ZMQ.Socket} object to destroy
*/
void closeSocket(Socket s)
{
if (s == null) {
return;
}
try {
s.internalClose();
}
finally {
sockets.remove(s);
}
}
/**
* Creates a selector. It needs to be closed by {@link #closeSelector(Selector)}.
*
* @return a newly created selector.
* @deprecated this was exposed by mistake.
*/
@Deprecated
public Selector createSelector()
{
return selector();
}
/**
* Creates a selector. Resource will be released when context will be closed.
*
* @return a newly created selector.
*/
Selector selector()
{
Selector selector = context.selector();
selectors.add(selector);
return selector;
}
/**
* Closes a selector.
* This is a DRAFT method, and may change without notice.
*
* @param selector the selector to close. It needs to have been created by {@link #createSelector()}.
* @deprecated {@link #createSelector()} was exposed by mistake. while waiting for the API to disappear, this method is provided to allow releasing resources.
*/
@Deprecated
@Draft
public void closeSelector(Selector selector)
{
if (selectors.remove(selector)) {
context.close(selector);
}
}
public Poller createPoller(int size)
{
return new Poller(context, size);
}
/**
* Creates new shadow context.
* Shares same underlying org.zeromq.Context instance but has own list
* of managed sockets, io thread count etc.
* @param ctx Original ZContext to create shadow of
* @return New ZContext
* @deprecated use the instance method directly
*/
@Deprecated
public static ZContext shadow(ZContext ctx)
{
return ctx.shadow();
}
/**
* Creates new shadow context.
* Shares same underlying org.zeromq.Context instance but has own list
* of managed sockets, io thread count etc.
* @return New ZContext
*/
public ZContext shadow()
{
if (! main) {
throw new IllegalStateException("Shadow contexts don't cast shadows");
}
ZContext context = new ZContext(this, ioThreads);
context.linger = linger;
context.sndhwm = sndhwm;
context.rcvhwm = rcvhwm;
context.pipehwm = pipehwm;
return context;
}
/**
* Create an attached thread, An attached thread gets a ctx and a PAIR pipe back to its
* parent. It must monitor its pipe, and exit if the pipe becomes unreadable
*
* @param runnable attached thread
* @param args forked runnable args
* @return pipe or null if there was an error
*/
public Socket fork(ZThread.IAttachedRunnable runnable, Object... args)
{
return ZThread.fork(this, runnable, args);
}
/**
* @return the ioThreads
*/
public int getIoThreads()
{
return ioThreads;
}
/**
* A deprecated function that does nothing.
*
* @param ioThreads the number of ioThreads to set
* @deprecated This value should not be changed after the context is initialized.
*/
@Deprecated
public void setIoThreads(int ioThreads)
{
}
/**
* @return the default linger for sockets.
*/
public int getLinger()
{
return linger;
}
/**
* @param linger the linger that will inherited by created socket.
*/
public void setLinger(int linger)
{
this.linger = linger;
}
/**
* Set initial receive HWM for all new normal sockets created in context.
* You can set this per-socket after the socket is created.
* The default, no matter the underlying ZeroMQ version, is 1,000.
* @param rcvhwm the rcvhwm
*/
public void setRcvHWM(int rcvhwm)
{
this.rcvhwm = rcvhwm;
}
/**
* Set initial receive HWM for all new normal sockets created in context.
* You can set this per-socket after the socket is created.
* The default, no matter the underlying ZeroMQ version, is 1,000.
* @param sndhwm the sndhwm
*/
public void setSndHWM(int sndhwm)
{
this.sndhwm = sndhwm;
}
/**
* Set the handler invoked when a {@link zmq.poll.Poller} abruptly terminates due to an uncaught exception.
* It default to the value of {@link Thread#getDefaultUncaughtExceptionHandler()}
* @param handler The object to use as this thread's uncaught exception handler. If null then this thread has no explicit handler.
* @throws IllegalStateException If context was already initialized by the creation of a socket
*/
public void setUncaughtExceptionHandler(UncaughtExceptionHandler handler)
{
context.setUncaughtExceptionHandler(handler);
}
/**
* @return The handler invoked when a {@link zmq.poll.Poller} abruptly terminates due to an uncaught exception.
*/
public UncaughtExceptionHandler getUncaughtExceptionHandler()
{
return context.getUncaughtExceptionHandler();
}
/**
* In {@link zmq.poll.Poller#run()}, some non-fatal exceptions can be thrown. This handler will be notified, so they can
* be logged.
* Default to {@link Throwable#printStackTrace()}
* @param handler The object to use as this thread's handler for recoverable exceptions notifications.
* @throws IllegalStateException If context was already initialized by the creation of a socket
*/
public void setNotificationExceptionHandler(UncaughtExceptionHandler handler)
{
context.setNotificationExceptionHandler(handler);
}
/**
* @return The handler invoked when a non-fatal exceptions is thrown in zmq.poll.Poller#run()
*/
public UncaughtExceptionHandler getNotificationExceptionHandler()
{
return context.getNotificationExceptionHandler();
}
/**
* Used to define a custom thread factory. It can be used to create thread that will be bounded to a CPU for
* performance or tweaks the created thread. It the UncaughtExceptionHandler is not set, the created thread UncaughtExceptionHandler
* will not be changed, so the factory can also be used to set it.
*
* @param threadFactory the thread factory used by {@link zmq.poll.Poller}
* @throws IllegalStateException If context was already initialized by the creation of a socket
*/
public void setThreadFactor(BiFunction threadFactory)
{
context.setThreadFactor(threadFactory);
}
/**
* @return the current thread factory
*/
public BiFunction getThreadFactory()
{
return context.getThreadFactory();
}
/**
* @return the main
*/
public boolean isMain()
{
return main;
}
/**
* @return true if no shadow context, no sockets and no selectors are alive.
*/
public boolean isEmpty()
{
return shadows.isEmpty() && sockets.isEmpty() && selectors.isEmpty();
}
/**
* @param main whether or not the context is being set to main
* @deprecated This value should not be changed after the context is initialized.
*/
@Deprecated
public void setMain(boolean main)
{
}
/**
* @return the context
*/
public Context getContext()
{
return context;
}
/**
* @param ctx sets the underlying zmq.Context associated with this ZContext wrapper object
* @deprecated This value should not be changed after the ZContext is initialized.
*/
@Deprecated
public void setContext(Context ctx)
{
}
/**
* Return a copy of the list of currently open sockets. Order is not meaningful.
* @return the sockets
*/
public List getSockets()
{
return new ArrayList<>(sockets);
}
@Override
public void close()
{
destroy();
}
public boolean isClosed()
{
return context.isClosed();
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZEvent.java 0000664 0000000 0000000 00000021557 14557711263 0021655 0 ustar 00root root 0000000 0000000 package org.zeromq;
import java.nio.channels.SelectableChannel;
import java.time.Duration;
import java.util.Objects;
import java.util.function.Function;
import org.zeromq.ZMQ.Socket;
import org.zeromq.ZMonitor.Event;
import zmq.ZError;
/**
* A high level wrapper for an event that stores all value as Enum or java object instead of integer, and associate a
* severity with them.
* The events are handled using the following rules.
*
*
* Events list
*
* Event
* Value type
* Severity level
*
*
* CONNECTED
* {@link java.nio.channels.SelectableChannel}
* debug
*
*
* CONNECT_DELAYED
* {@link ZMQ.Error} or null if no error
* debug
*
*
* CONNECT_RETRIED
* {@link java.time.Duration}
* debug
*
*
* LISTENING
* {@link java.nio.channels.SelectableChannel}
* debug
*
*
* BIND_FAILED
* {@link ZMQ.Error} or null if no error
* error
*
*
* ACCEPTED
* {@link java.nio.channels.SelectableChannel}
* debug
*
*
* ACCEPT_FAILED
* {@link ZMQ.Error} or null if no error
* error
*
*
* CLOSED
* {@link java.nio.channels.SelectableChannel}
* debug
*
*
* CLOSE_FAILED
* {@link ZMQ.Error} or null if no error
* error
*
*
* DISCONNECTED
* {@link java.nio.channels.SelectableChannel}
* info
*
*
* MONITOR_STOPPED
* null value
* debug
*
*
* HANDSHAKE_FAILED_NO_DETAIL
* {@link ZMQ.Error} or null if no error
* error
*
*
* HANDSHAKE_SUCCEEDED
* {@link ZMQ.Error} or null if no error
* debug
*
*
* HANDSHAKE_FAILED_PROTOCOL
* {@link ZMonitor.ProtocolCode}
* error
*
*
* HANDSHAKE_FAILED_AUTH
* {@link java.lang.Integer}
* warn
*
*
* HANDSHAKE_PROTOCOL
* {@link java.lang.Integer}
* debug
*
*
*/
public class ZEvent
{
/**
* An interface used to consume events in monitor
*/
public interface ZEventConsummer extends zmq.ZMQ.EventConsummer
{
void consume(ZEvent ev);
default void consume(zmq.ZMQ.Event event)
{
consume(new ZEvent(event, SelectableChannel.class::cast));
}
}
private final Event event;
// To keep backward compatibility, the old value field only store integer
// The resolved value (Error, channel or other) is stored in resolvedValue field.
private final Object value;
private final String address;
private ZEvent(zmq.ZMQ.Event event, Function getResolveChannel)
{
this.event = ZMonitor.Event.findByCode(event.event);
this.address = event.addr;
this.value = resolve(this.event, event.arg, getResolveChannel);
}
static Object resolve(Event event, Object value, Function getResolveChannel)
{
switch (event) {
case HANDSHAKE_FAILED_PROTOCOL:
return ZMonitor.ProtocolCode.findByCode((Integer) value);
case CLOSE_FAILED:
case ACCEPT_FAILED:
case BIND_FAILED:
case HANDSHAKE_FAILED_NO_DETAIL:
case CONNECT_DELAYED:
case HANDSHAKE_SUCCEEDED:
return ZMQ.Error.findByCode((Integer) value);
case HANDSHAKE_FAILED_AUTH:
case HANDSHAKE_PROTOCOL:
return value;
case CONNECTED:
case LISTENING:
case ACCEPTED:
case CLOSED:
case DISCONNECTED:
return getResolveChannel.apply(value);
case CONNECT_RETRIED:
return Duration.ofMillis((Integer) value);
case MONITOR_STOPPED:
return null;
default:
assert false : "Unhandled event " + event;
return null;
}
}
public Event getEvent()
{
return event;
}
/**
* Return the value of the event as a high level java object.
* It returns objects of type:
*
* {@link org.zeromq.ZMonitor.ProtocolCode} for a handshake protocol error.
* {@link org.zeromq.ZMQ.Error} for any other error.
* {@link Duration} when associated with a delay.
* null when no relevant value available.
*
* @param The expected type of the returned object
* @return The resolved value.
*/
@SuppressWarnings("unchecked")
public M getValue()
{
return (M) value;
}
public String getAddress()
{
return address;
}
/**
* Used to check if the event is an error.
*
* Generally, any event that define the errno is
* considered as an error.
* @return true if the event was an error
*/
public boolean isError()
{
switch (event) {
case BIND_FAILED:
case ACCEPT_FAILED:
case CLOSE_FAILED:
case HANDSHAKE_FAILED_NO_DETAIL:
case HANDSHAKE_FAILED_PROTOCOL:
return true;
default:
return false;
}
}
/**
* Used to check if the event is a warning.
*
* Generally, any event that return an authentication failure is
* considered as a warning.
* @return true if the event was a warning
*/
public boolean isWarn()
{
return event == Event.HANDSHAKE_FAILED_AUTH;
}
/**
* Used to check if the event is an information.
*
* Generally, any event that return an authentication failure is
* considered as a warning.
* @return true if the event was a warning
*/
public boolean isInformation()
{
return event == Event.DISCONNECTED;
}
/**
* Used to check if the event is an error.
*
* Generally, any event that define the errno is
* considered as an error.
* @return true if the event was an error
*/
public boolean isDebug()
{
switch (event) {
case CONNECTED:
case CONNECT_DELAYED:
case CONNECT_RETRIED:
case LISTENING:
case ACCEPTED:
case CLOSED:
case MONITOR_STOPPED:
case HANDSHAKE_SUCCEEDED:
case HANDSHAKE_PROTOCOL:
return true;
default:
return false;
}
}
@Override
public boolean equals(Object o)
{
if (this == o) {
return true;
}
else if (o == null || getClass() != o.getClass()) {
return false;
}
else {
ZEvent zEvent = (ZEvent) o;
return event == zEvent.event && Objects.equals(value, zEvent.value) && address.equals(zEvent.address);
}
}
@Override
public int hashCode()
{
return Objects.hash(event, value, address);
}
@Override
public String toString()
{
return "ZEvent{" + "event=" + event + ", value=" + value + ", address='" + address + '\'' + '}';
}
/**
* Receive an event from a monitor socket.
*
* @param socket the monitor socket
* @param flags the flags to apply to the read operation.
* @return the received event or null if no message was received.
* @throws ZMQException In case of errors with the monitor socket
*/
public static ZEvent recv(Socket socket, int flags)
{
zmq.ZMQ.Event e = zmq.ZMQ.Event.read(socket.base(), flags);
if (socket.errno() > 0 && socket.errno() != ZError.EAGAIN) {
throw new ZMQException(socket.errno());
}
else if (e == null) {
return null;
}
else {
return new ZEvent(e, o -> e.getChannel(socket.getCtx()));
}
}
/**
* Receive an event from a monitor socket.
* Does a blocking recv.
*
* @param socket the monitor socket
* @return the received event or null if no message was received.
* @throws ZMQException In case of errors with the monitor socket
*/
public static ZEvent recv(ZMQ.Socket socket)
{
return recv(socket, 0);
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZFrame.java 0000664 0000000 0000000 00000024451 14557711263 0021622 0 ustar 00root root 0000000 0000000 package org.zeromq;
import java.nio.charset.Charset;
import java.util.Arrays;
import org.zeromq.ZMQ.Socket;
import org.zeromq.util.ZData;
import zmq.Msg;
import zmq.SocketBase;
/**
* ZFrame
*
* The ZFrame class provides methods to send and receive single message
* frames across 0MQ sockets. A 'frame' corresponds to one underlying zmq_msg_t in the libzmq code.
* When you read a frame from a socket, the more() method indicates if the frame is part of an
* unfinished multipart message. The send() method normally destroys the frame, but with the ZFRAME_REUSE flag, you can send
* the same frame many times. Frames are binary, and this class has no special support for text data.
*
*/
public class ZFrame
{
public static final int MORE = ZMQ.SNDMORE;
public static final int REUSE = 128; // no effect at java
public static final int DONTWAIT = ZMQ.DONTWAIT;
private boolean more;
private byte[] data;
private int routingId;
private String group;
/**
* Class Constructor
* Creates an empty frame.
* (Useful when reading frames from a 0MQ Socket)
*/
protected ZFrame()
{
}
/**
* Class Constructor
* Copies message data into ZFrame object
* @param data
* Data to copy into ZFrame object
*/
public ZFrame(byte[] data)
{
if (data != null) {
this.data = data;
}
}
/**
* Class Constructor
* Copies String into frame data
* @param data
* String to copy into ZFrame object as bytes, decoded using {@link ZMQ#CHARSET}
*/
public ZFrame(String data)
{
if (data != null) {
this.data = data.getBytes(ZMQ.CHARSET);
}
}
/**
* Class Constructor
* Uses internal Msg class to access routingId
* @param msg internal Msg class to copy into Zframe
*/
protected ZFrame(zmq.Msg msg)
{
if (msg == null) {
return;
}
this.data = msg.data();
this.more = msg.hasMore();
this.routingId = msg.getRoutingId();
}
/**
* Return frame routing ID, if the frame came from a ZMQ_SERVER socket.
* Else returns zero.
* @return the routing ID
*/
public int getRoutingId()
{
return routingId;
}
/**
* Set routing ID on frame. This is used if/when the frame is sent to a
* ZMQ_SERVER socket.
* @param routingId the routing ID
*/
public void setRoutingId(int routingId)
{
this.routingId = routingId;
}
/**
* Gets the group used for RADIO/DISH sockets.
* @return the group name, or null.
*/
public String getGroup()
{
return group;
}
/**
* Sets the group used for RADIO/DISH sockets.
* @param group the group name, or null to unset it.
*/
public void setGroup(String group)
{
this.group = group;
}
/**
* Destructor.
*/
public void destroy()
{
if (hasData()) {
data = null;
}
}
/**
* @return the data
*/
public byte[] getData()
{
return data;
}
public String getString(Charset charset)
{
if (!hasData()) {
return "";
}
return new String(data, charset);
}
/**
* @return More flag, true if last read had MORE message parts to come
*/
public boolean hasMore()
{
return more;
}
/**
* Returns byte size of frame, if set, else 0
* @return
* Number of bytes in frame data, else 0
*/
public int size()
{
if (hasData()) {
return data.length;
}
else {
return 0;
}
}
/**
* Convenience method to ascertain if this frame contains some message data
* @return
* True if frame contains data
*/
public boolean hasData()
{
return data != null;
}
/**
* Internal method to call org.zeromq.Socket send() method.
* @param socket
* 0MQ socket to send on
* @param flags
* Valid send() method flags, defined in org.zeromq.ZMQ class
* @return
* True if success, else False
*/
public boolean send(Socket socket, int flags)
{
Utils.checkArgument(socket != null, "socket parameter must be set");
final SocketBase base = socket.base();
final zmq.Msg msg = new Msg(data);
if (group != null) {
msg.setGroup(group);
}
int sendFlags = (flags & ZFrame.MORE) == ZFrame.MORE ? zmq.ZMQ.ZMQ_SNDMORE : 0;
sendFlags |= (flags & ZFrame.DONTWAIT) == ZFrame.DONTWAIT ? zmq.ZMQ.ZMQ_DONTWAIT : 0;
// Only set the routerId if the socket is a ZMQ_Server
if (base instanceof zmq.socket.clientserver.Server) {
msg.setRoutingId(this.routingId);
}
return base.send(msg, sendFlags);
}
/**
* Sends frame to socket if it contains any data.
* Frame contents are kept after the send.
* @param socket
* 0MQ socket to send frame
* @param flags
* Valid send() method flags, defined in org.zeromq.ZMQ class
* @return
* True if success, else False
*/
public boolean sendAndKeep(Socket socket, int flags)
{
return send(socket, flags);
}
/**
* Sends frame to socket if it contains any data.
* Frame contents are kept after the send.
* Uses default behaviour of Socket.send() method, with no flags set
* @param socket
* 0MQ socket to send frame
* @return
* True if success, else False
*/
public boolean sendAndKeep(Socket socket)
{
return sendAndKeep(socket, 0);
}
/**
* Sends frame to socket if it contains data.
* Use this method to send a frame and destroy the data after.
* @param socket
* 0MQ socket to send frame
* @param flags
* Valid send() method flags, defined in org.zeromq.ZMQ class
* @return
* True if success, else False
*/
public boolean sendAndDestroy(Socket socket, int flags)
{
boolean ret = send(socket, flags);
if (ret) {
destroy();
}
return ret;
}
/**
* Sends frame to socket if it contains data.
* Use this method to send an isolated frame and destroy the data after.
* Uses default behaviour of Socket.send() method, with no flags set
* @param socket
* 0MQ socket to send frame
* @return
* True if success, else False
*/
public boolean sendAndDestroy(Socket socket)
{
return sendAndDestroy(socket, 0);
}
/**
* Creates a new frame that duplicates an existing frame
* @return
* Duplicate of frame; message contents copied into new byte array
*/
public ZFrame duplicate()
{
return new ZFrame(this.data);
}
/**
* Returns true if both frames have byte - for byte identical data
* @param other
* The other ZFrame to compare
* @return
* True if both ZFrames have same byte-identical data, else false
*/
public boolean hasSameData(ZFrame other)
{
if (other == null) {
return false;
}
if (size() == other.size()) {
return Arrays.equals(data, other.data);
}
return false;
}
/**
* Sets new contents for frame
* @param data
* New byte array contents for frame
*/
public void reset(String data)
{
this.data = data.getBytes(ZMQ.CHARSET);
}
/**
* Sets new contents for frame
* @param data
* New byte array contents for frame
*/
public void reset(byte[] data)
{
this.data = data;
}
/**
* @return frame data as a printable hex string
*/
public String strhex()
{
return ZData.strhex(data);
}
/**
* String equals.
* Uses String compareTo for the comparison (lexigraphical)
* @param str
* String to compare with frame data
* @return
* True if frame body data matches given string
*/
public boolean streq(String str)
{
return ZData.streq(data, str);
}
@Override
public boolean equals(Object o)
{
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ZFrame zFrame = (ZFrame) o;
return Arrays.equals(data, zFrame.data);
}
@Override
public int hashCode()
{
return Arrays.hashCode(data);
}
/**
* Returns a human - readable representation of frame's data
* @return
* A text string or hex-encoded string if data contains any non-printable ASCII characters
*/
@Override
public String toString()
{
return ZData.toString(data);
}
/**
* Receives single frame from socket, returns the received frame object, or null if the recv
* was interrupted. Does a blocking recv, if you want to not block then use
* recvFrame(socket, ZMQ.DONTWAIT);
*
* @param socket
* Socket to read from
* @return
* received frame, else null
*/
public static ZFrame recvFrame(Socket socket)
{
return ZFrame.recvFrame(socket, 0);
}
/**
* Receive a new frame off the socket, Returns newly-allocated frame, or
* null if there was no input waiting, or if the read was interrupted.
* @param socket
* Socket to read from
* @param flags
* Pass flags to 0MQ socket.recv call
* @return
* received frame, else null
*/
public static ZFrame recvFrame(Socket socket, int flags)
{
final SocketBase base = socket.base();
zmq.Msg msg = base.recv(flags);
if (msg == null) {
// Check to see if there was an error in recv
socket.mayRaise();
return null;
}
ZFrame frame = new ZFrame(msg);
frame.setGroup(msg.getGroup());
return frame;
}
public void print(String prefix)
{
ZData.print(System.out, prefix, getData(), size());
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZLoop.java 0000664 0000000 0000000 00000030677 14557711263 0021510 0 ustar 00root root 0000000 0000000 package org.zeromq;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.zeromq.ZMQ.Context;
import org.zeromq.ZMQ.PollItem;
import org.zeromq.ZMQ.Poller;
import zmq.util.Objects;
/**
* The ZLoop class provides an event-driven reactor pattern. The reactor
* handles zmq.PollItem items (pollers or writers, sockets or fds), and
* once-off or repeated timers. Its resolution is 1 msec. It uses a tickless
* timer to reduce CPU interrupts in inactive processes.
*/
public class ZLoop
{
public interface IZLoopHandler
{
int handle(ZLoop loop, PollItem item, Object arg);
}
private static class SPoller
{
final PollItem item;
final IZLoopHandler handler;
final Object arg;
int errors; // If too many errors, kill poller
protected SPoller(PollItem item, IZLoopHandler handler, Object arg)
{
this.item = item;
this.handler = handler;
this.arg = arg;
errors = 0;
}
}
private static class STimer
{
final int delay;
int times;
final IZLoopHandler handler;
final Object arg;
long when; // Clock time when alarm goes off
public STimer(int delay, int times, IZLoopHandler handler, Object arg)
{
this.delay = delay;
this.times = times;
this.handler = handler;
this.arg = arg;
this.when = -1;
}
}
private final Context context; // Context managing the pollers.
private final List pollers; // List of poll items
private final List timers; // List of timers
private int pollSize; // Size of poll set
private Poller pollset; // zmq_poll set
private SPoller[] pollact; // Pollers for this poll set
private boolean dirty; // True if pollset needs rebuilding
private boolean verbose; // True if verbose tracing wanted
private final List zombies; // List of timers to kill
private final List newTimers; // List of timers to add
public ZLoop(Context context)
{
Objects.requireNonNull(context, "Context has to be supplied for ZLoop");
this.context = context;
pollers = new ArrayList<>();
timers = new ArrayList<>();
zombies = new ArrayList<>();
newTimers = new ArrayList<>();
}
public ZLoop(ZContext ctx)
{
this(ctx.getContext());
}
/**
* @deprecated no-op behaviour
*/
@Deprecated
public void destroy()
{
// do nothing
}
// We hold an array of pollers that matches the pollset, so we can
// register/cancel pollers orthogonally to executing the pollset
// activity on pollers. Returns 0 on success, -1 on failure.
private void rebuild()
{
pollact = null;
pollSize = pollers.size();
if (pollset != null) {
pollset.close();
}
pollset = context.poller(pollSize);
assert (pollset != null);
pollact = new SPoller[pollSize];
int itemNbr = 0;
for (SPoller poller : pollers) {
pollset.register(poller.item);
pollact[itemNbr] = poller;
itemNbr++;
}
dirty = false;
}
private long ticklessTimer()
{
// Calculate tickless timer, up to 1 hour
long tickless = System.currentTimeMillis() + 1000 * 3600;
for (STimer timer : timers) {
if (timer.when == -1) {
timer.when = timer.delay + System.currentTimeMillis();
}
if (tickless > timer.when) {
tickless = timer.when;
}
}
long timeout = tickless - System.currentTimeMillis();
if (timeout < 0) {
timeout = 0;
}
if (verbose) {
System.out.printf("I: zloop: polling for %d msec\n", timeout);
}
return timeout;
}
// --------------------------------------------------------------------------
// Register pollitem with the reactor. When the pollitem is ready, will call
// the handler, passing the arg. Returns 0 if OK, -1 if there was an error.
// If you register the pollitem more than once, each instance will invoke its
// corresponding handler.
public int addPoller(PollItem pollItem, IZLoopHandler handler, Object arg)
{
if (pollItem.getRawSocket() == null && pollItem.getSocket() == null) {
return -1;
}
SPoller poller = new SPoller(pollItem, handler, arg);
pollers.add(poller);
dirty = true;
if (verbose) {
System.out.printf(
"I: zloop: register %s poller (%s, %s)\n",
pollItem.getSocket() != null ? pollItem.getSocket().getType() : "RAW",
pollItem.getSocket(),
pollItem.getRawSocket());
}
return 0;
}
// --------------------------------------------------------------------------
// Cancel a pollitem from the reactor, specified by socket or FD. If both
// are specified, uses only socket. If multiple poll items exist for same
// socket/FD, cancels ALL of them.
public void removePoller(PollItem pollItem)
{
Iterator it = pollers.iterator();
while (it.hasNext()) {
SPoller p = it.next();
if (pollItem.equals(p.item)) {
it.remove();
dirty = true;
}
}
if (verbose) {
System.out.printf(
"I: zloop: cancel %s poller (%s, %s)",
pollItem.getSocket() != null ? pollItem.getSocket().getType() : "RAW",
pollItem.getSocket(),
pollItem.getRawSocket());
}
}
// --------------------------------------------------------------------------
// Register a timer that expires after some delay and repeats some number of
// times. At each expiry, will call the handler, passing the arg. To
// run a timer forever, use 0 times. Returns 0 if OK, -1 if there was an
// error.
public int addTimer(int delay, int times, IZLoopHandler handler, Object arg)
{
STimer timer = new STimer(delay, times, handler, arg);
// We cannot touch self->timers because we may be executing that
// from inside the poll loop. So, we hold the new timer on the newTimers
// list, and process that list when we're done executing timers.
newTimers.add(timer);
if (verbose) {
System.out.printf("I: zloop: register timer delay=%d times=%d\n", delay, times);
}
return 0;
}
// --------------------------------------------------------------------------
// Cancel all timers for a specific argument (as provided in zloop_timer)
// Returns 0 on success.
public int removeTimer(Object arg)
{
Objects.requireNonNull(arg, "Argument has to be supplied");
// We cannot touch self->timers because we may be executing that
// from inside the poll loop. So, we hold the arg on the zombie
// list, and process that list when we're done executing timers.
zombies.add(arg);
if (verbose) {
System.out.print("I: zloop: cancel timer\n");
}
return 0;
}
// --------------------------------------------------------------------------
// Set verbose tracing of reactor on/off
public void verbose(boolean verbose)
{
this.verbose = verbose;
}
// --------------------------------------------------------------------------
// Start the reactor. Takes control of the thread and returns when the 0MQ
// context is terminated or the process is interrupted, or any event handler
// returns -1. Event handlers may register new sockets and timers, and
// cancel sockets. Returns 0 if interrupted, -1 if cancelled by a
// handler, positive on internal error
public int start()
{
int rc = 0;
timers.addAll(newTimers);
newTimers.clear();
// Recalculate all timers now
for (STimer timer : timers) {
timer.when = timer.delay + System.currentTimeMillis();
}
// Main reactor loop
while (!Thread.currentThread().isInterrupted()) {
if (dirty) {
// If s_rebuild_pollset() fails, break out of the loop and
// return its error
rebuild();
}
long wait = ticklessTimer();
rc = pollset.poll(wait);
if (rc == -1) {
if (verbose) {
System.out.printf("I: zloop: interrupted (%d)\n", rc);
}
rc = 0;
break; // Context has been shut down
}
// Handle any timers that have now expired
Iterator it = timers.iterator();
while (it.hasNext()) {
STimer timer = it.next();
if (System.currentTimeMillis() >= timer.when && timer.when != -1) {
if (verbose) {
System.out.println("I: zloop: call timer handler");
}
rc = timer.handler.handle(this, null, timer.arg);
if (rc == -1) {
break; // Timer handler signaled break
}
if (timer.times != 0 && --timer.times == 0) {
it.remove();
}
else {
timer.when = timer.delay + System.currentTimeMillis();
}
}
}
if (rc == -1) {
break; // Some timer signalled break from the reactor loop
}
// Handle any pollers that are ready
for (int itemNbr = 0; itemNbr < pollSize; itemNbr++) {
SPoller poller = pollact[itemNbr];
if (pollset.getItem(itemNbr).isError()) {
if (verbose) {
System.out.printf(
"I: zloop: can't poll %s socket (%s, %s)\n",
poller.item.getSocket() != null ? poller.item.getSocket().getType() : "RAW",
poller.item.getSocket(),
poller.item.getRawSocket());
}
// Give handler one chance to handle error, then kill
// poller because it'll disrupt the reactor otherwise.
if (poller.errors++ > 0) {
removePoller(poller.item);
}
}
else {
poller.errors = 0; // A non-error happened
}
if (pollset.getItem(itemNbr).readyOps() > 0) {
if (verbose) {
System.out.printf(
"I: zloop: call %s socket handler (%s, %s)\n",
poller.item.getSocket() != null ? poller.item.getSocket().getType() : "RAW",
poller.item.getSocket(),
poller.item.getRawSocket());
}
rc = poller.handler.handle(this, poller.item, poller.arg);
if (rc == -1) {
break; // Poller handler signaled break
}
}
}
// Now handle any timer zombies
// This is going to be slow if we have many zombies
for (Object arg : zombies) {
it = timers.iterator();
while (it.hasNext()) {
STimer timer = it.next();
if (timer.arg == arg) {
it.remove();
}
}
}
// Now handle any new timers added inside the loop
timers.addAll(newTimers);
newTimers.clear();
if (rc == -1) {
break;
}
}
return rc;
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZMQ.java 0000664 0000000 0000000 00000573542 14557711263 0021117 0 ustar 00root root 0000000 0000000 package org.zeromq;
import java.io.Closeable;
import java.lang.Thread.UncaughtExceptionHandler;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.Selector;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.zeromq.proto.ZPicture;
import zmq.Ctx;
import zmq.Msg;
import zmq.Options;
import zmq.SocketBase;
import zmq.ZError;
import zmq.ZError.CtxTerminatedException;
import zmq.io.coder.IDecoder;
import zmq.io.coder.IEncoder;
import zmq.io.mechanism.Mechanisms;
import zmq.io.net.SelectorProviderChooser;
import zmq.msg.MsgAllocator;
import zmq.util.Draft;
import zmq.util.Z85;
import zmq.util.function.BiFunction;
import zmq.util.function.Consumer;
/**
* The ØMQ lightweight messaging kernel is a library which extends the standard socket interfaces
* with features traditionally provided by specialised messaging middleware products.
* ØMQ sockets provide an abstraction of asynchronous message queues, multiple messaging patterns,
* message filtering (subscriptions), seamless access to multiple transport protocols and more.
*
* Following is an overview of ØMQ concepts, describes how ØMQ abstracts standard sockets
* and provides a reference manual for the functions provided by the ØMQ library.
*
* Contexts
* Before using any ØMQ library functions you must create a {@link ZMQ.Context ØMQ context} using {@link ZMQ#context(int)}.
* When you exit your application you must destroy the context using {@link ZMQ.Context#close()}.
*
* Thread safety
* A ØMQ context is thread safe and may be shared among as many application threads as necessary,
* without any additional locking required on the part of the caller.
*
* Individual ØMQ sockets are not thread safe except in the case
* where full memory barriers are issued when migrating a socket from one thread to another.
*
* In practice this means applications can create a socket in one thread with * {@link ZMQ.Context#socket(SocketType)}
* and then pass it to a newly created thread as part of thread initialization.
*
*
Multiple contexts
* Multiple contexts may coexist within a single application.
*
* Thus, an application can use ØMQ directly and at the same time make use of any number of additional libraries
* or components which themselves make use of ØMQ as long as the above guidelines regarding thread safety are adhered to.
*
*
Messages
* A ØMQ message is a discrete unit of data passed between applications or components of the same application.
* ØMQ messages have no internal structure and from the point of view of ØMQ itself
* they are considered to be opaque binary data.
*
*
Sockets
* {@link ZMQ.Socket ØMQ sockets} present an abstraction of a asynchronous message queue,
* with the exact queueing semantics depending on the socket type in use.
*
*
Transports
* A ØMQ socket can use multiple different underlying transport mechanisms.
* Each transport mechanism is suited to a particular purpose and has its own advantages and drawbacks.
*
* The following transport mechanisms are provided:
*
* Unicast transport using TCP
* Local inter-process communication transport
* Local in-process (inter-thread) communication transport
*
*
* Proxies
* ØMQ provides proxies to create fanout and fan-in topologies.
* A proxy connects a frontend socket to a backend socket
* and switches all messages between the two sockets, opaquely.
* A proxy may optionally capture all traffic to a third socket.
*
* Security
* A ØMQ socket can select a security mechanism. Both peers must use the same security mechanism.
*
* The following security mechanisms are provided for IPC and TCP connections:
*
* Null security
* Plain-text authentication using username and password
* Elliptic curve authentication and encryption
*
*/
public class ZMQ
{
/**
* Socket flag to indicate that more message parts are coming.
*/
public static final int SNDMORE = zmq.ZMQ.ZMQ_SNDMORE;
// Values for flags in Socket's send and recv functions.
/**
* Socket flag to indicate a nonblocking send or recv mode.
*/
public static final int DONTWAIT = zmq.ZMQ.ZMQ_DONTWAIT;
public static final int NOBLOCK = zmq.ZMQ.ZMQ_DONTWAIT;
// Socket types, used when creating a Socket. Note that all of the int types here is
// deprecated, use SocketType instead
@Deprecated
public static final int PAIR = zmq.ZMQ.ZMQ_PAIR;
@Deprecated
public static final int PUB = zmq.ZMQ.ZMQ_PUB;
@Deprecated
public static final int SUB = zmq.ZMQ.ZMQ_SUB;
@Deprecated
public static final int REQ = zmq.ZMQ.ZMQ_REQ;
@Deprecated
public static final int REP = zmq.ZMQ.ZMQ_REP;
@Deprecated
public static final int DEALER = zmq.ZMQ.ZMQ_DEALER;
/**
* Old alias for DEALER flag.
* Flag to specify a XREQ socket, receiving side must be a XREP.
*
* @deprecated As of release 3.0 of zeromq, replaced by {@link #DEALER}
*/
@Deprecated
public static final int XREQ = DEALER;
@Deprecated
public static final int ROUTER = zmq.ZMQ.ZMQ_ROUTER;
/**
* Old alias for ROUTER flag.
* Flag to specify the receiving part of a XREQ socket.
*
* @deprecated As of release 3.0 of zeromq, replaced by {@link #ROUTER}
*/
@Deprecated
public static final int XREP = ROUTER;
@Deprecated
public static final int PULL = zmq.ZMQ.ZMQ_PULL;
@Deprecated
public static final int PUSH = zmq.ZMQ.ZMQ_PUSH;
@Deprecated
public static final int XPUB = zmq.ZMQ.ZMQ_XPUB;
@Deprecated
public static final int XSUB = zmq.ZMQ.ZMQ_XSUB;
@Deprecated
public static final int STREAM = zmq.ZMQ.ZMQ_STREAM;
/**
* Flag to specify a STREAMER device.
*/
@Deprecated
public static final int STREAMER = zmq.ZMQ.ZMQ_STREAMER;
/**
* Flag to specify a FORWARDER device.
*/
@Deprecated
public static final int FORWARDER = zmq.ZMQ.ZMQ_FORWARDER;
/**
* Flag to specify a QUEUE device.
*/
@Deprecated
public static final int QUEUE = zmq.ZMQ.ZMQ_QUEUE;
/**
* @see org.zeromq.ZMQ#PULL
*/
@Deprecated
public static final int UPSTREAM = PULL;
/**
* @see org.zeromq.ZMQ#PUSH
*/
@Deprecated
public static final int DOWNSTREAM = PUSH;
/**
* EVENT_CONNECTED: connection established.
* The EVENT_CONNECTED event triggers when a connection has been
* established to a remote peer. This can happen either synchronous
* or asynchronous. Value is the FD of the newly connected socket.
*/
public static final int EVENT_CONNECTED = zmq.ZMQ.ZMQ_EVENT_CONNECTED;
/**
* EVENT_CONNECT_DELAYED: synchronous connect failed, it's being polled.
* The EVENT_CONNECT_DELAYED event triggers when an immediate connection
* attempt is delayed and its completion is being polled for. Value has
* no meaning.
*/
public static final int EVENT_CONNECT_DELAYED = zmq.ZMQ.ZMQ_EVENT_CONNECT_DELAYED;
/**
* @see org.zeromq.ZMQ#EVENT_CONNECT_DELAYED
*/
@Deprecated
public static final int EVENT_DELAYED = EVENT_CONNECT_DELAYED;
/**
* EVENT_CONNECT_RETRIED: asynchronous connect / reconnection attempt.
* The EVENT_CONNECT_RETRIED event triggers when a connection attempt is
* being handled by reconnect timer. The reconnect interval's recomputed
* for each attempt. Value is the reconnect interval.
*/
public static final int EVENT_CONNECT_RETRIED = zmq.ZMQ.ZMQ_EVENT_CONNECT_RETRIED;
/**
* @see org.zeromq.ZMQ#EVENT_CONNECT_RETRIED
*/
@Deprecated
public static final int EVENT_RETRIED = EVENT_CONNECT_RETRIED;
/**
* EVENT_LISTENING: socket bound to an address, ready to accept connections.
* The EVENT_LISTENING event triggers when a socket's successfully bound to
* a an interface. Value is the FD of the newly bound socket.
*/
public static final int EVENT_LISTENING = zmq.ZMQ.ZMQ_EVENT_LISTENING;
/**
* EVENT_BIND_FAILED: socket could not bind to an address.
* The EVENT_BIND_FAILED event triggers when a socket could not bind to a
* given interface. Value is the errno generated by the bind call.
*/
public static final int EVENT_BIND_FAILED = zmq.ZMQ.ZMQ_EVENT_BIND_FAILED;
/**
* EVENT_ACCEPTED: connection accepted to bound interface.
* The EVENT_ACCEPTED event triggers when a connection from a remote peer
* has been established with a socket's listen address. Value is the FD of
* the accepted socket.
*/
public static final int EVENT_ACCEPTED = zmq.ZMQ.ZMQ_EVENT_ACCEPTED;
/**
* EVENT_ACCEPT_FAILED: could not accept client connection.
* The EVENT_ACCEPT_FAILED event triggers when a connection attempt to a
* socket's bound address fails. Value is the errno generated by accept.
*/
public static final int EVENT_ACCEPT_FAILED = zmq.ZMQ.ZMQ_EVENT_ACCEPT_FAILED;
/**
* EVENT_CLOSED: connection closed.
* The EVENT_CLOSED event triggers when a connection's underlying
* descriptor has been closed. Value is the former FD of the for the
* closed socket. FD has been closed already!
*/
public static final int EVENT_CLOSED = zmq.ZMQ.ZMQ_EVENT_CLOSED;
/**
* EVENT_CLOSE_FAILED: connection couldn't be closed.
* The EVENT_CLOSE_FAILED event triggers when a descriptor could not be
* released back to the OS. Implementation note: ONLY FOR IPC SOCKETS.
* Value is the errno generated by unlink.
*/
public static final int EVENT_CLOSE_FAILED = zmq.ZMQ.ZMQ_EVENT_CLOSE_FAILED;
/**
* EVENT_DISCONNECTED: broken session.
* The EVENT_DISCONNECTED event triggers when the stream engine (tcp and
* ipc specific) detects a corrupted / broken session. Value is the FD of
* the socket.
*/
public static final int EVENT_DISCONNECTED = zmq.ZMQ.ZMQ_EVENT_DISCONNECTED;
/**
* EVENT_MONITOR_STOPPED: monitor has been stopped.
* The EVENT_MONITOR_STOPPED event triggers when the monitor for a socket is
* stopped.
*/
public static final int EVENT_MONITOR_STOPPED = zmq.ZMQ.ZMQ_EVENT_MONITOR_STOPPED;
/**
* EVENT_HANDSHAKE_PROTOCOL: protocol has been successfully negotiated.
* The EVENT_HANDSHAKE_PROTOCOL event triggers when the stream engine (tcp and ipc)
* successfully negotiated a protocol version with the peer. Value is the version number
* (0 for unversioned, 3 for V3).
*/
public static final int EVENT_HANDSHAKE_PROTOCOL = zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_PROTOCOL;
/**
* EVENT_ALL: all events known.
* The EVENT_ALL constant can be used to set up a monitor for all known events.
*/
public static final int EVENT_ALL = zmq.ZMQ.ZMQ_EVENT_ALL;
/**
* Unspecified system errors during handshake. Event value is an errno.
*/
public static final int HANDSHAKE_FAILED_NO_DETAIL = zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL;
/**
* Handshake complete successfully with successful authentication (if
* enabled). Event value is unused.
*/
public static final int HANDSHAKE_SUCCEEDED = zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_SUCCEEDED;
/**
* Protocol errors between ZMTP peers or between server and ZAP handler.
* Event value is one of ZMQ_PROTOCOL_ERROR_*
*/
public static final int HANDSHAKE_FAILED_PROTOCOL = zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL;
/**
* Failed authentication requests. Event value is the numeric ZAP status
* code, i.e. 300, 400 or 500.
*/
public static final int HANDSHAKE_FAILED_AUTH = zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_FAILED_AUTH;
public static final byte[] MESSAGE_SEPARATOR = zmq.ZMQ.MESSAGE_SEPARATOR;
public static final byte[] SUBSCRIPTION_ALL = zmq.ZMQ.SUBSCRIPTION_ALL;
public static final byte[] PROXY_PAUSE = zmq.ZMQ.PROXY_PAUSE;
public static final byte[] PROXY_RESUME = zmq.ZMQ.PROXY_RESUME;
public static final byte[] PROXY_TERMINATE = zmq.ZMQ.PROXY_TERMINATE;
public static final Charset CHARSET = zmq.ZMQ.CHARSET;
private ZMQ()
{
}
/**
* Create a new Context.
*
* @param ioThreads Number of threads to use, usually 1 is sufficient for most use cases.
* @return the Context
*/
public static Context context(int ioThreads)
{
return new Context(ioThreads);
}
@Deprecated
public static boolean device(int type, Socket frontend, Socket backend)
{
return zmq.ZMQ.proxy(frontend.base, backend.base, null);
}
/**
* Starts the built-in 0MQ proxy in the current application thread.
* The proxy connects a frontend socket to a backend socket. Conceptually, data flows from frontend to backend.
* Depending on the socket types, replies may flow in the opposite direction. The direction is conceptual only;
* the proxy is fully symmetric and there is no technical difference between frontend and backend.
*
* Before calling ZMQ.proxy() you must set any socket options, and connect or bind both frontend and backend sockets.
* The two conventional proxy models are:
*
* ZMQ.proxy() runs in the current thread and returns only if/when the current context is closed.
*
* @param frontend ZMQ.Socket
* @param backend ZMQ.Socket
* @param capture If the capture socket is not NULL, the proxy shall send all messages, received on both
* frontend and backend, to the capture socket. The capture socket should be a
* ZMQ_PUB, ZMQ_DEALER, ZMQ_PUSH, or ZMQ_PAIR socket.
*/
public static boolean proxy(Socket frontend, Socket backend, Socket capture)
{
return zmq.ZMQ.proxy(frontend.base, backend.base, capture != null ? capture.base : null);
}
public static boolean proxy(Socket frontend, Socket backend, Socket capture, Socket control)
{
return zmq.ZMQ.proxy(
frontend.base,
backend.base,
capture == null ? null : capture.base,
control == null ? null : control.base);
}
public static int poll(Selector selector, PollItem[] items, long timeout)
{
return poll(selector, items, items.length, timeout);
}
public static int poll(Selector selector, PollItem[] items, int count, long timeout)
{
zmq.poll.PollItem[] pollItems = new zmq.poll.PollItem[count];
for (int i = 0; i < count; i++) {
pollItems[i] = items[i].base;
}
return zmq.ZMQ.poll(selector, pollItems, count, timeout);
}
/**
* @return Major version number of the ZMQ library.
*/
public static int getMajorVersion()
{
return zmq.ZMQ.ZMQ_VERSION_MAJOR;
}
/**
* @return Major version number of the ZMQ library.
*/
public static int getMinorVersion()
{
return zmq.ZMQ.ZMQ_VERSION_MINOR;
}
/**
* @return Major version number of the ZMQ library.
*/
public static int getPatchVersion()
{
return zmq.ZMQ.ZMQ_VERSION_PATCH;
}
/**
* @return Full version number of the ZMQ library used for comparing versions.
*/
public static int getFullVersion()
{
return zmq.ZMQ.makeVersion(zmq.ZMQ.ZMQ_VERSION_MAJOR, zmq.ZMQ.ZMQ_VERSION_MINOR, zmq.ZMQ.ZMQ_VERSION_PATCH);
}
/**
* @param major Version major component.
* @param minor Version minor component.
* @param patch Version patch component.
* @return Comparible single int version number.
*/
public static int makeVersion(final int major, final int minor, final int patch)
{
return zmq.ZMQ.makeVersion(major, minor, patch);
}
/**
* @return String version number in the form major.minor.patch.
*/
public static String getVersionString()
{
return "" + zmq.ZMQ.ZMQ_VERSION_MAJOR + "." + zmq.ZMQ.ZMQ_VERSION_MINOR + "." + zmq.ZMQ.ZMQ_VERSION_PATCH;
}
public static void msleep(long millis)
{
zmq.ZMQ.msleep(millis);
}
public static void sleep(long seconds)
{
zmq.ZMQ.sleep(seconds);
}
public static void sleep(long amount, TimeUnit unit)
{
zmq.ZMQ.sleep(amount, unit);
}
/**
* Resolve code from errornumber.
*
* Messages are taken from https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
*/
public enum Error
{
NOERROR(0, "No error"),
ENOTSUP(ZError.ENOTSUP, "Not supported"),
EPROTONOSUPPORT(ZError.EPROTONOSUPPORT, "Protocol not supported"),
ENOBUFS(ZError.ENOBUFS, "No buffer space available"),
ENETDOWN(ZError.ENETDOWN, "Network is down"),
EADDRINUSE(ZError.EADDRINUSE, "Address already in use"),
EADDRNOTAVAIL(ZError.EADDRNOTAVAIL, "Address not available"),
ECONNREFUSED(ZError.ECONNREFUSED, "Connection refused"),
EINPROGRESS(ZError.EINPROGRESS, "Operation in progress"),
EHOSTUNREACH(ZError.EHOSTUNREACH, "Host unreachable"),
EMTHREAD(ZError.EMTHREAD, "No thread available"),
EFSM(ZError.EFSM, "Operation cannot be accomplished in current state"),
ENOCOMPATPROTO(ZError.ENOCOMPATPROTO, "The protocol is not compatible with the socket type"),
ETERM(ZError.ETERM, "Context was terminated"),
ENOTSOCK(ZError.ENOTSOCK, "Not a socket"),
EAGAIN(ZError.EAGAIN, "Resource unavailable, try again"),
ENOENT(ZError.ENOENT, "No such file or directory"),
EINTR(ZError.EINTR, "Interrupted function"),
EACCESS(ZError.EACCESS, "Permission denied"),
EFAULT(ZError.EFAULT, "Bad address"),
EINVAL(ZError.EINVAL, "Invalid argument"),
EISCONN(ZError.EISCONN, "Socket is connected"),
ENOTCONN(ZError.ENOTCONN, "The socket is not connected"),
EMSGSIZE(ZError.EMSGSIZE, "Message too large"),
EAFNOSUPPORT(ZError.EAFNOSUPPORT, "Address family not supported"),
ENETUNREACH(ZError.ENETUNREACH, "Network unreachable"),
ECONNABORTED(ZError.ECONNABORTED, "Connection aborted"),
ECONNRESET(ZError.ECONNRESET, "Connection reset"),
ETIMEDOUT(ZError.ETIMEDOUT, "Connection timed out"),
ENETRESET(ZError.ENETRESET, "Connection aborted by network"),
EIOEXC(ZError.EIOEXC),
ESOCKET(ZError.ESOCKET),
EMFILE(ZError.EMFILE, "File descriptor value too large"),
EPROTO(ZError.EPROTO, "Protocol error");
private static final Map map = new HashMap<>(Error.values().length);
static {
for (Error e : Error.values()) {
map.put(e.code, e);
}
}
private final int code;
private final String message;
Error(int code)
{
this.code = code;
this.message = "errno " + code;
}
Error(int code, String message)
{
this.code = code;
this.message = message;
}
public static Error findByCode(int code)
{
if (code <= 0) {
return NOERROR;
}
else if (map.containsKey(code)) {
return map.get(code);
}
else {
throw new IllegalArgumentException("Unknown " + Error.class.getName() + " enum code: " + code);
}
}
public int getCode()
{
return code;
}
public String getMessage()
{
return message;
}
}
/**
* Container for all sockets in a single process,
* acting as the transport for inproc sockets,
* which are the fastest way to connect threads in one process.
*/
public static class Context implements Closeable
{
private final AtomicBoolean closed = new AtomicBoolean(false);
private final Ctx ctx;
/**
* Class constructor.
*
* @param ioThreads size of the threads pool to handle I/O operations.
*/
protected Context(int ioThreads)
{
ctx = zmq.ZMQ.init(ioThreads);
}
/**
* Returns true if terminate() has been called on ctx.
*/
public boolean isTerminated()
{
return !ctx.isActive();
}
/**
* The size of the 0MQ thread pool to handle I/O operations.
*/
public int getIOThreads()
{
return ctx.get(zmq.ZMQ.ZMQ_IO_THREADS);
}
/**
* Set the size of the 0MQ thread pool to handle I/O operations.
* @throws IllegalStateException If context was already initialized by the creation of a socket
*/
public boolean setIOThreads(int ioThreads)
{
return ctx.set(zmq.ZMQ.ZMQ_IO_THREADS, ioThreads);
}
/**
* The maximum number of sockets allowed on the context
*/
public int getMaxSockets()
{
return ctx.get(zmq.ZMQ.ZMQ_MAX_SOCKETS);
}
/**
* Sets the maximum number of sockets allowed on the context
* @throws IllegalStateException If context was already initialized by the creation of a socket
*/
public boolean setMaxSockets(int maxSockets)
{
return ctx.set(zmq.ZMQ.ZMQ_MAX_SOCKETS, maxSockets);
}
/**
* @deprecated use {@link #isBlocky()} instead
*/
@Deprecated
public boolean getBlocky()
{
return isBlocky();
}
public boolean isBlocky()
{
return ctx.get(zmq.ZMQ.ZMQ_BLOCKY) != 0;
}
public boolean setBlocky(boolean block)
{
return ctx.set(zmq.ZMQ.ZMQ_BLOCKY, block ? 1 : 0);
}
public boolean isIPv6()
{
return ctx.get(zmq.ZMQ.ZMQ_IPV6) != 0;
}
public boolean getIPv6()
{
return isIPv6();
}
public boolean setIPv6(boolean ipv6)
{
return ctx.set(zmq.ZMQ.ZMQ_IPV6, ipv6 ? 1 : 0);
}
/**
* Set the handler invoked when a {@link zmq.poll.Poller} abruptly terminates due to an uncaught exception.
* It default to the value of {@link Thread#getDefaultUncaughtExceptionHandler()}
* @param handler The object to use as this thread's uncaught exception handler. If null then this thread has no explicit handler.
* @throws IllegalStateException If context was already initialized by the creation of a socket
*/
public void setUncaughtExceptionHandler(UncaughtExceptionHandler handler)
{
ctx.setUncaughtExceptionHandler(handler);
}
/**
* @return The handler invoked when a {@link zmq.poll.Poller} abruptly terminates due to an uncaught exception.
*/
public UncaughtExceptionHandler getUncaughtExceptionHandler()
{
return ctx.getUncaughtExceptionHandler();
}
/**
* In {@link zmq.poll.Poller#run()}, some non-fatal exceptions can be thrown. This handler will be notified, so they can
* be logged.
* Default to {@link Throwable#printStackTrace()}
* @param handler The object to use as this thread's handler for recoverable exceptions notifications.
* @throws IllegalStateException If context was already initialized by the creation of a socket
*/
public void setNotificationExceptionHandler(UncaughtExceptionHandler handler)
{
ctx.setNotificationExceptionHandler(handler);
}
/**
* @return The handler invoked when a non-fatal exceptions is thrown in zmq.poll.Poller#run()
*/
public UncaughtExceptionHandler getNotificationExceptionHandler()
{
return ctx.getNotificationExceptionHandler();
}
/**
* Used to define a custom thread factory. It can be used to create thread that will be bounded to a CPU for
* performance or tweaks the created thread. It the UncaughtExceptionHandler is not set, the created thread UncaughtExceptionHandler
* will not be changed, so the factory can also be used to set it.
*
* @param threadFactory the thread factory used by {@link zmq.poll.Poller}
* @throws IllegalStateException If context was already initialized by the creation of a socket
*/
public void setThreadFactor(BiFunction threadFactory)
{
ctx.setThreadFactory(threadFactory);
}
/**
* @return the current thread factory
*/
public BiFunction getThreadFactory()
{
return ctx.getThreadFactory();
}
/**
* This is an explicit "destructor". It can be called to ensure the corresponding 0MQ
* Context has been disposed of.
*/
public void term()
{
if (closed.compareAndSet(false, true)) {
ctx.terminate();
}
}
public boolean isClosed()
{
return closed.get();
}
/**
* Creates a ØMQ socket within the specified context and return an opaque handle to the newly created socket.
*
* The type argument specifies the socket type, which determines the semantics of communication over the socket.
*
* The newly created socket is initially unbound, and not associated with any endpoints.
*
* In order to establish a message flow a socket must first be connected
* to at least one endpoint with {@link org.zeromq.ZMQ.Socket#connect(String)},
* or at least one endpoint must be created for accepting incoming connections with {@link org.zeromq.ZMQ.Socket#bind(String)}.
*
* @param type the socket type.
* @return the newly created Socket.
*/
public Socket socket(SocketType type)
{
return new Socket(this, type);
}
@Deprecated
public Socket socket(int type)
{
return socket(SocketType.type(type));
}
/**
* Create a new Selector within this context.
*
* @return the newly created Selector.
*/
public Selector selector()
{
return ctx.createSelector();
}
/**
* Closes a Selector that was created within this context.
*
* @param selector the Selector to close.
* @return true if the selector was closed. otherwise false
* (mostly because it was not created by the context).
*/
public boolean close(Selector selector)
{
return ctx.closeSelector(selector);
}
/**
* Create a new Poller within this context, with a default size.
* DO NOT FORGET TO CLOSE THE POLLER AFTER USE with {@link Poller#close()}
*
* @return the newly created Poller.
*/
public Poller poller()
{
return new Poller(this);
}
/**
* Create a new Poller within this context, with a specified initial size.
* DO NOT FORGET TO CLOSE THE POLLER AFTER USE with {@link Poller#close()}
*
* @param size the poller initial size.
* @return the newly created Poller.
*/
public Poller poller(int size)
{
return new Poller(this, size);
}
/**
* Destroys the ØMQ context context.
* Context termination is performed in the following steps:
*
* Any blocking operations currently in progress on sockets open within context
* shall return immediately with an error code of ETERM.
* With the exception of {@link ZMQ.Socket#close()}, any further operations on sockets
* open within context shall fail with an error code of ETERM.
* After interrupting all blocking calls, this method shall block until the following conditions are satisfied:
*
* All sockets open within context have been closed with {@link ZMQ.Socket#close()}.
* For each socket within context, all messages sent by the application with {@link ZMQ.Socket#send} have either
* been physically transferred to a network peer,
* or the socket's linger period set with the {@link ZMQ.Socket#setLinger(int)} socket option has expired.
*
*
*
* Warning
*
* As ZMQ_LINGER defaults to "infinite", by default this method will block indefinitely if there are any pending connects or sends.
* We strongly recommend to
*
* set ZMQ_LINGER to zero on all sockets
* close all sockets, before calling this method
*
*/
@Override
public void close()
{
term();
}
}
/**
* Abstracts an asynchronous message queue, with the exact queuing semantics depending on the socket type in use.
*
* Where conventional sockets transfer streams of bytes or discrete datagrams, ØMQ sockets transfer discrete messages.
* Key differences to conventional sockets
* Generally speaking, conventional sockets present a synchronous interface to either
* connection-oriented reliable byte streams (SOCK_STREAM),
* or connection-less unreliable datagrams (SOCK_DGRAM).
*
* In comparison, ØMQ sockets present an abstraction of an asynchronous message queue,
* with the exact queueing semantics depending on the socket type in use.
* Where conventional sockets transfer streams of bytes or discrete datagrams, ØMQ sockets transfer discrete messages.
*
* ØMQ sockets being asynchronous means that the timings of the physical connection setup and tear down, reconnect and effective delivery
* are transparent to the user and organized by ØMQ itself.
* Further, messages may be queued in the event that a peer is unavailable to receive them.
*
* Conventional sockets allow only strict one-to-one (two peers), many-to-one (many clients, one server), or in some cases one-to-many (multicast) relationships.
* With the exception of {@link ZMQ#PAIR}, ØMQ sockets may be connected to multiple endpoints using {@link ZMQ.Socket#connect(String)},
* while simultaneously accepting incoming connections from multiple endpoints bound to the socket using {@link ZMQ.Socket#bind(String)},
* thus allowing many-to-many relationships.
* Thread safety
* ØMQ sockets are not thread safe. Applications MUST NOT use a socket from multiple threads
* except after migrating a socket from one thread to another with a "full fence" memory barrier.
*
* ØMQ sockets are not Thread.interrupt safe. Applications MUST NOT interrupt threads using ØMQ sockets .
* Messaging patterns
*
* Request-reply
*
* The request-reply pattern is used for sending requests from a {@link ZMQ#REQ} client to one or more {@link ZMQ#REP} services, and receiving subsequent replies to each request sent.
* The request-reply pattern is formally defined by http://rfc.zeromq.org/spec:28.
* {@link ZMQ#REQ}, {@link ZMQ#REP}, {@link ZMQ#DEALER}, {@link ZMQ#ROUTER} socket types belong to this pattern.
*
* Publish-subscribe
*
* The publish-subscribe pattern is used for one-to-many distribution of data from a single publisher to multiple subscribers in a fan out fashion.
* The publish-subscribe pattern is formally defined by http://rfc.zeromq.org/spec:29.
* {@link ZMQ#SUB}, {@link ZMQ#PUB}, {@link ZMQ#XSUB}, {@link ZMQ#XPUB} socket types belong to this pattern.
*
* Pipeline
*
* The pipeline pattern is used for distributing data to nodes arranged in a pipeline. Data always flows down the pipeline, and each stage of the pipeline is connected to at least one node.
* When a pipeline stage is connected to multiple nodes data is round-robined among all connected nodes.
* The pipeline pattern is formally defined by http://rfc.zeromq.org/spec:30.
* {@link ZMQ#PUSH}, {@link ZMQ#PULL} socket types belong to this pattern.
*
* Exclusive pair
*
* The exclusive pair pattern is used to connect a peer to precisely one other peer. This pattern is used for inter-thread communication across the inproc transport,
* using {@link ZMQ#PAIR} socket type.
* The exclusive pair pattern is formally defined by http://rfc.zeromq.org/spec:31.
*
* Native
*
* The native pattern is used for communicating with TCP peers and allows asynchronous requests and replies in either direction,
* using {@link ZMQ#STREAM} socket type.
*
*
*/
public static class Socket implements Closeable
{
// This port range is defined by IANA for dynamic or private ports
// We use this when choosing a port for dynamic binding.
private static final int DYNFROM = 0xc000;
private static final int DYNTO = 0xffff;
private final Consumer socketClose;
private final SocketBase base;
private final AtomicBoolean isClosed = new AtomicBoolean(false);
/**
* Class constructor.
*
* @param context a 0MQ context previously created.
* @param type the socket type.
*/
protected Socket(Context context, SocketType type)
{
this(context.ctx, type.type, null);
}
/**
* Class constructor.
*
* @param context a 0MQ context previously created.
* @param type the socket type.
*/
protected Socket(ZContext context, SocketType type)
{
this(context.getContext().ctx, type.type, context::closeSocket);
}
/**
* Class constructor.
*
* @param context a 0MQ context previously created.
* @param type the socket type.
* @deprecated use {@link Socket#Socket(Context, SocketType)}
*/
@Deprecated
protected Socket(Context context, int type)
{
this(context.ctx, type, null);
}
/**
* Wrap an already existing socket
* @param base an already generated socket
*/
protected Socket(SocketBase base)
{
this.socketClose = s -> internalClose();
this.base = base;
}
private Socket(Ctx ctx, int type, Consumer socketClose)
{
this.base = ctx.createSocket(type);
this.socketClose = socketClose != null ? socketClose : s -> internalClose();
}
/**
* DO NOT USE if you're trying to build a special proxy
*
* @return raw zmq.SocketBase
*/
public SocketBase base()
{
return base;
}
/**
* This is an explicit "destructor". It can be called to ensure the corresponding 0MQ Socket
* has been disposed of. If the socket was created from a org.zeromq.ZContext, it will remove
* the reference to this socket from it.
*/
@Override
public void close()
{
socketClose.accept(this);
}
void internalClose()
{
if (isClosed.compareAndSet(false, true)) {
base.close();
}
}
/**
* The 'ZMQ_TYPE option shall retrieve the socket type for the specified
* 'socket'. The socket type is specified at socket creation time and
* cannot be modified afterwards.
*
* @return the socket type.
*/
public int getType()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_TYPE);
}
/**
* The 'ZMQ_TYPE option shall retrieve the socket type for the specified
* 'socket'. The socket type is specified at socket creation time and
* cannot be modified afterwards.
*
* @return the socket type as an enum.
*/
public SocketType getSocketType()
{
return SocketType.type(getType());
}
/**
*
* @return the low level {@link Ctx} associated with this socket.
*/
public Ctx getCtx()
{
return base.getCtx();
}
/**
* The 'ZMQ_LINGER' option shall retrieve the period for pending outbound
* messages to linger in memory after closing the socket. Value of -1 means
* infinite. Pending messages will be kept until they are fully transferred to
* the peer. Value of 0 means that all the pending messages are dropped immediately
* when socket is closed. Positive value means number of milliseconds to keep
* trying to send the pending messages before discarding them.
*
* @return the linger period.
* @see #setLinger(int)
*/
public int getLinger()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_LINGER);
}
private boolean setSocketOpt(int option, Object value)
{
try {
boolean set = base.setSocketOpt(option, value);
set &= base.errno() != ZError.EINVAL;
return set;
}
catch (CtxTerminatedException e) {
return false;
}
}
/**
* The ZMQ_LINGER option shall set the linger period for the specified socket.
* The linger period determines how long pending messages which have yet to be sent to a peer
* shall linger in memory after a socket is disconnected with disconnect or closed with close,
* and further affects the termination of the socket's context with Ctx#term.
* The following outlines the different behaviours:
* A value of -1 specifies an infinite linger period.
* Pending messages shall not be discarded after a call to disconnect() or close();
* attempting to terminate the socket's context with Ctx#term() shall block until all pending messages have been sent to a peer.
*
* The value of 0 specifies no linger period. Pending messages shall be discarded immediately after a call to disconnect() or close().
*
* Positive values specify an upper bound for the linger period in milliseconds.
* Pending messages shall not be discarded after a call to disconnect() or close();
* attempting to terminate the socket's context with Ctx#term() shall block until either all pending messages have been sent to a peer,
* or the linger period expires, after which any pending messages shall be discarded.
*
* @param value the linger period in milliseconds.
* @return true if the option was set, otherwise false
* @see #getLinger()
* @deprecated the linger option has only integer range, use {@link #setLinger(int)} instead
*/
@Deprecated
public boolean setLinger(long value)
{
return setLinger(Long.valueOf(value).intValue());
}
/**
* The ZMQ_LINGER option shall set the linger period for the specified socket.
* The linger period determines how long pending messages which have yet to be sent to a peer
* shall linger in memory after a socket is disconnected with disconnect or closed with close,
* and further affects the termination of the socket's context with Ctx#term.
* The following outlines the different behaviours:
*
A value of -1 specifies an infinite linger period.
* Pending messages shall not be discarded after a call to disconnect() or close();
* attempting to terminate the socket's context with Ctx#term() shall block until all pending messages have been sent to a peer.
*
* The value of 0 specifies no linger period. Pending messages shall be discarded immediately after a call to disconnect() or close().
*
* Positive values specify an upper bound for the linger period in milliseconds.
* Pending messages shall not be discarded after a call to disconnect() or close();
* attempting to terminate the socket's context with Ctx#term() shall block until either all pending messages have been sent to a peer,
* or the linger period expires, after which any pending messages shall be discarded.
*
* @param value the linger period in milliseconds.
* @return true if the option was set, otherwise false
* @see #getLinger()
*/
public boolean setLinger(int value)
{
return base.setSocketOpt(zmq.ZMQ.ZMQ_LINGER, value);
}
/**
* The ZMQ_RECONNECT_IVL option shall retrieve the initial reconnection interval for the specified socket.
* The reconnection interval is the period ØMQ shall wait between attempts to reconnect
* disconnected peers when using connection-oriented transports.
* The value -1 means no reconnection.
*
* CAUTION: The reconnection interval may be randomized by ØMQ to prevent reconnection storms in topologies with a large number of peers per socket.
*
* @return the reconnectIVL.
* @see #setReconnectIVL(int)
*/
public int getReconnectIVL()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_RECONNECT_IVL);
}
/**
* The ZMQ_RECONNECT_IVL option shall set the initial reconnection interval for the specified socket.
* The reconnection interval is the period ØMQ shall wait between attempts
* to reconnect disconnected peers when using connection-oriented transports.
* The value -1 means no reconnection.
*
* @return true if the option was set, otherwise false
* @see #getReconnectIVL()
* @deprecated reconnect interval option uses integer range, use {@link #setReconnectIVL(int)} instead
*/
@Deprecated
public boolean setReconnectIVL(long value)
{
return setReconnectIVL(Long.valueOf(value).intValue());
}
/**
* The ZMQ_RECONNECT_IVL option shall set the initial reconnection interval for the specified socket.
* The reconnection interval is the period ØMQ shall wait between attempts
* to reconnect disconnected peers when using connection-oriented transports.
* The value -1 means no reconnection.
*
* @return true if the option was set, otherwise false.
* @see #getReconnectIVL()
*/
public boolean setReconnectIVL(int value)
{
return base.setSocketOpt(zmq.ZMQ.ZMQ_RECONNECT_IVL, value);
}
/**
* The ZMQ_BACKLOG option shall retrieve the maximum length of the queue
* of outstanding peer connections for the specified socket;
* this only applies to connection-oriented transports.
* For details refer to your operating system documentation for the listen function.
*
* @return the the maximum length of the queue of outstanding peer connections.
* @see #setBacklog(int)
*/
public int getBacklog()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_BACKLOG);
}
/**
* The ZMQ_BACKLOG option shall set the maximum length
* of the queue of outstanding peer connections for the specified socket;
* this only applies to connection-oriented transports.
* For details refer to your operating system documentation for the listen function.
*
* @param value the maximum length of the queue of outstanding peer connections.
* @return true if the option was set, otherwise false.
* @see #getBacklog()
* @deprecated this option uses integer range, use {@link #setBacklog(int)} instead.
*/
@Deprecated
public boolean setBacklog(long value)
{
return setBacklog(Long.valueOf(value).intValue());
}
/**
* The ZMQ_BACKLOG option shall set the maximum length
* of the queue of outstanding peer connections for the specified socket;
* this only applies to connection-oriented transports.
* For details refer to your operating system documentation for the listen function.
*
* @param value the maximum length of the queue of outstanding peer connections.
* @return true if the option was set, otherwise false.
* @see #getBacklog()
*/
public boolean setBacklog(int value)
{
return setSocketOpt(zmq.ZMQ.ZMQ_BACKLOG, value);
}
/**
* The ZMQ_HANDSHAKE_IVL option shall retrieve the maximum handshake interval
* for the specified socket.
* Handshaking is the exchange of socket configuration information
* (socket type, identity, security) that occurs when a connection is first opened,
* only for connection-oriented transports.
* If handshaking does not complete within the configured time,
* the connection shall be closed. The value 0 means no handshake time limit.
*
* @return the maximum handshake interval.
* @see #setHandshakeIvl(int)
*/
public int getHandshakeIvl()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_HANDSHAKE_IVL);
}
/**
* The ZMQ_HEARTBEAT_IVL option shall set the interval
* between sending ZMTP heartbeats for the specified socket.
* If this option is set and is greater than 0,
* then a PING ZMTP command will be sent every ZMQ_HEARTBEAT_IVL milliseconds.
*
* @return heartbeat interval in milliseconds
*/
public int getHeartbeatIvl()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_HEARTBEAT_IVL);
}
/**
* The ZMQ_HEARTBEAT_TIMEOUT option shall set
* how long to wait before timing-out a connection
* after sending a PING ZMTP command and not receiving any traffic.
* This option is only valid if ZMQ_HEARTBEAT_IVL is also set,
* and is greater than 0. The connection will time out
* if there is no traffic received after sending the PING command,
* but the received traffic does not have to be a PONG command
* - any received traffic will cancel the timeout.
*
* @return heartbeat timeout in milliseconds
*/
public int getHeartbeatTimeout()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_HEARTBEAT_TIMEOUT);
}
/**
* The ZMQ_HEARTBEAT_TTL option shall set the timeout
* on the remote peer for ZMTP heartbeats.
* If this option is greater than 0,
* the remote side shall time out the connection
* if it does not receive any more traffic within the TTL period.
* This option does not have any effect if ZMQ_HEARTBEAT_IVL is not set or is 0.
* Internally, this value is rounded down to the nearest decisecond,
* any value less than 100 will have no effect.
*
* @return heartbeat time-to-live in milliseconds
*/
public int getHeartbeatTtl()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_HEARTBEAT_TTL);
}
/**
* The ZMQ_HEARTBEAT_CONTEXT option shall set the ping context
* of the peer for ZMTP heartbeats.
*
* This API is in DRAFT state and is subject to change at ANY time until declared stable.
*
* If this option is set, every ping message sent for heartbeat will contain this context.
*
* @return the context to be sent with ping messages. Empty array by default.
*/
@Draft
public byte[] getHeartbeatContext()
{
return (byte[]) base.getSocketOptx(zmq.ZMQ.ZMQ_HEARTBEAT_CONTEXT);
}
/**
* The ZMQ_HANDSHAKE_IVL option shall set the maximum handshake interval for the specified socket.
* Handshaking is the exchange of socket configuration information (socket type, identity, security)
* that occurs when a connection is first opened, only for connection-oriented transports.
* If handshaking does not complete within the configured time, the connection shall be closed.
* The value 0 means no handshake time limit.
*
* @param maxHandshakeIvl the maximum handshake interval
* @return true if the option was set, otherwise false
* @see #getHandshakeIvl()
*/
public boolean setHandshakeIvl(int maxHandshakeIvl)
{
return setSocketOpt(zmq.ZMQ.ZMQ_HANDSHAKE_IVL, maxHandshakeIvl);
}
/**
* The ZMQ_HEARTBEAT_IVL option shall set the interval
* between sending ZMTP heartbeats for the specified socket.
* If this option is set and is greater than 0,
* then a PING ZMTP command will be sent every ZMQ_HEARTBEAT_IVL milliseconds.
*
* @param heartbeatIvl heartbeat interval in milliseconds
* @return true if the option was set, otherwise false
*/
public boolean setHeartbeatIvl(int heartbeatIvl)
{
return setSocketOpt(zmq.ZMQ.ZMQ_HEARTBEAT_IVL, heartbeatIvl);
}
/**
* The ZMQ_HEARTBEAT_TIMEOUT option shall set
* how long to wait before timing-out a connection
* after sending a PING ZMTP command and not receiving any traffic.
* This option is only valid if ZMQ_HEARTBEAT_IVL is also set,
* and is greater than 0. The connection will time out
* if there is no traffic received after sending the PING command,
* but the received traffic does not have to be a PONG command
* - any received traffic will cancel the timeout.
*
* @param heartbeatTimeout heartbeat timeout in milliseconds
* @return true if the option was set, otherwise false
*/
public boolean setHeartbeatTimeout(int heartbeatTimeout)
{
return setSocketOpt(zmq.ZMQ.ZMQ_HEARTBEAT_TIMEOUT, heartbeatTimeout);
}
/**
* The ZMQ_HEARTBEAT_TTL option shall set the timeout
* on the remote peer for ZMTP heartbeats.
* If this option is greater than 0,
* the remote side shall time out the connection
* if it does not receive any more traffic within the TTL period.
* This option does not have any effect if ZMQ_HEARTBEAT_IVL is not set or is 0.
* Internally, this value is rounded down to the nearest decisecond,
* any value less than 100 will have no effect.
*
* @param heartbeatTtl heartbeat time-to-live in milliseconds
* @return true if the option was set, otherwise false
*/
public boolean setHeartbeatTtl(int heartbeatTtl)
{
return setSocketOpt(zmq.ZMQ.ZMQ_HEARTBEAT_TTL, heartbeatTtl);
}
/**
* The ZMQ_HEARTBEAT_CONTEXT option shall set the ping context
* of the peer for ZMTP heartbeats.
*
* This API is in DRAFT state and is subject to change at ANY time until declared stable.
*
* If this option is set, every ping message sent for heartbeat will contain this context.
*
* @param pingContext the context to be sent with ping messages.
* @return true if the option was set, otherwise false
*/
@Draft
public boolean setHeartbeatContext(byte[] pingContext)
{
return setSocketOpt(zmq.ZMQ.ZMQ_HEARTBEAT_CONTEXT, pingContext);
}
/**
* Retrieve the IP_TOS option for the socket.
*
* @return the value of the Type-Of-Service set for the socket.
* @see #setTos(int)
*/
public int getTos()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_TOS);
}
/**
* Sets the ToS fields (Differentiated services (DS)
* and Explicit Congestion Notification (ECN) field of the IP header.
* The ToS field is typically used to specify a packets priority.
* The availability of this option is dependent on intermediate network equipment
* that inspect the ToS field andprovide a path for low-delay, high-throughput, highly-reliable service, etc.
*
* @return true if the option was set, otherwise false.
* @see #getTos()
*/
public boolean setTos(int value)
{
return setSocketOpt(zmq.ZMQ.ZMQ_TOS, value);
}
/**
* The ZMQ_RECONNECT_IVL_MAX option shall retrieve the maximum reconnection interval for the specified socket.
* This is the maximum period ØMQ shall wait between attempts to reconnect.
* On each reconnect attempt, the previous interval shall be doubled untill ZMQ_RECONNECT_IVL_MAX is reached.
* This allows for exponential backoff strategy.
* Default value means no exponential backoff is performed and reconnect interval calculations are only based on ZMQ_RECONNECT_IVL.
*
* @return the reconnectIVLMax.
* @see #setReconnectIVLMax(int)
*/
public int getReconnectIVLMax()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_RECONNECT_IVL_MAX);
}
/**
* The ZMQ_RECONNECT_IVL_MAX option shall set the maximum reconnection interval for the specified socket.
* This is the maximum period ØMQ shall wait between attempts to reconnect.
* On each reconnect attempt, the previous interval shall be doubled until ZMQ_RECONNECT_IVL_MAX is reached.
* This allows for exponential backoff strategy.
* Default value means no exponential backoff is performed and reconnect interval calculations are only based on ZMQ_RECONNECT_IVL.
*
* @return true if the option was set, otherwise false
* @see #getReconnectIVLMax()
* @deprecated this option uses integer range, use {@link #setReconnectIVLMax(int)} instead
*/
@Deprecated
public boolean setReconnectIVLMax(long value)
{
return setReconnectIVLMax(Long.valueOf(value).intValue());
}
/**
* The ZMQ_RECONNECT_IVL_MAX option shall set the maximum reconnection interval for the specified socket.
* This is the maximum period ØMQ shall wait between attempts to reconnect.
* On each reconnect attempt, the previous interval shall be doubled until ZMQ_RECONNECT_IVL_MAX is reached.
* This allows for exponential backoff strategy.
* Default value means no exponential backoff is performed and reconnect interval calculations are only based on ZMQ_RECONNECT_IVL.
*
* @return true if the option was set, otherwise false
* @see #getReconnectIVLMax()
*/
public boolean setReconnectIVLMax(int value)
{
return setSocketOpt(zmq.ZMQ.ZMQ_RECONNECT_IVL_MAX, value);
}
/**
* The option shall retrieve limit for the inbound messages.
* If a peer sends a message larger than ZMQ_MAXMSGSIZE it is disconnected.
* Value of -1 means no limit.
*
* @return the maxMsgSize.
* @see #setMaxMsgSize(long)
*/
public long getMaxMsgSize()
{
return (Long) base.getSocketOptx(zmq.ZMQ.ZMQ_MAXMSGSIZE);
}
/**
* Limits the size of the inbound message.
* If a peer sends a message larger than ZMQ_MAXMSGSIZE it is disconnected.
* Value of -1 means no limit.
*
* @return true if the option was set, otherwise false
* @see #getMaxMsgSize()
*/
public boolean setMaxMsgSize(long value)
{
return setSocketOpt(zmq.ZMQ.ZMQ_MAXMSGSIZE, value);
}
/**
* The ZMQ_SNDHWM option shall return the high water mark for outbound messages on the specified socket.
* The high water mark is a hard limit on the maximum number of outstanding messages ØMQ
* shall queue in memory for any single peer that the specified socket is communicating with.
* A value of zero means no limit.
* If this limit has been reached the socket shall enter an exceptional state and depending on the socket type,
* ØMQ shall take appropriate action such as blocking or dropping sent messages.
* Refer to the individual socket descriptions in zmq_socket(3) for details on the exact action taken for each socket type.
*
* @return the SndHWM.
* @see #setSndHWM(int)
*/
public int getSndHWM()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_SNDHWM);
}
/**
* The ZMQ_SNDHWM option shall set the high water mark for outbound messages on the specified socket.
* The high water mark is a hard limit on the maximum number of outstanding messages ØMQ
* shall queue in memory for any single peer that the specified socket is communicating with.
* A value of zero means no limit.
* If this limit has been reached the socket shall enter an exceptional state and depending on the socket type,
* ØMQ shall take appropriate action such as blocking or dropping sent messages.
* Refer to the individual socket descriptions in zmq_socket(3) for details on the exact action taken for each socket type.
*
* CAUTION: ØMQ does not guarantee that the socket will accept as many as ZMQ_SNDHWM messages,
* and the actual limit may be as much as 60-70% lower depending on the flow of messages on the socket.
*
* @return true if the option was set, otherwise false.
* @see #getSndHWM()
* @deprecated this option uses integer range, use {@link #setSndHWM(int)} instead
*/
@Deprecated
public boolean setSndHWM(long value)
{
return setSndHWM(Long.valueOf(value).intValue());
}
/**
* The ZMQ_SNDHWM option shall set the high water mark for outbound messages on the specified socket.
* The high water mark is a hard limit on the maximum number of outstanding messages ØMQ
* shall queue in memory for any single peer that the specified socket is communicating with.
* A value of zero means no limit.
* If this limit has been reached the socket shall enter an exceptional state and depending on the socket type,
* ØMQ shall take appropriate action such as blocking or dropping sent messages.
* Refer to the individual socket descriptions in zmq_socket(3) for details on the exact action taken for each socket type.
*
* CAUTION: ØMQ does not guarantee that the socket will accept as many as ZMQ_SNDHWM messages,
* and the actual limit may be as much as 60-70% lower depending on the flow of messages on the socket.
*
* @param value
* @return true if the option was set, otherwise false.
* @see #getSndHWM()
*/
public boolean setSndHWM(int value)
{
return setSocketOpt(zmq.ZMQ.ZMQ_SNDHWM, value);
}
/**
* The ZMQ_RCVHWM option shall return the high water mark for inbound messages on the specified socket.
* The high water mark is a hard limit on the maximum number of outstanding messages ØMQ
* shall queue in memory for any single peer that the specified socket is communicating with.
* A value of zero means no limit.
* If this limit has been reached the socket shall enter an exceptional state and depending on the socket type,
* ØMQ shall take appropriate action such as blocking or dropping sent messages.
* Refer to the individual socket descriptions in zmq_socket(3) for details on the exact action taken for each socket type.
*
* @return the recvHWM period.
* @see #setRcvHWM(int)
*/
public int getRcvHWM()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_RCVHWM);
}
/**
* The ZMQ_RCVHWM option shall set the high water mark for inbound messages on the specified socket.
* The high water mark is a hard limit on the maximum number of outstanding messages ØMQ
* shall queue in memory for any single peer that the specified socket is communicating with.
* A value of zero means no limit.
* If this limit has been reached the socket shall enter an exceptional state and depending on the socket type,
* ØMQ shall take appropriate action such as blocking or dropping sent messages.
* Refer to the individual socket descriptions in zmq_socket(3) for details on the exact action taken for each socket type.
*
* @return true if the option was set, otherwise false
* @see #getRcvHWM()
* @deprecated this option uses integer range, use {@link #setRcvHWM(int)} instead
*/
@Deprecated
public boolean setRcvHWM(long value)
{
return setRcvHWM(Long.valueOf(value).intValue());
}
/**
* The ZMQ_RCVHWM option shall set the high water mark for inbound messages on the specified socket.
* The high water mark is a hard limit on the maximum number of outstanding messages ØMQ
* shall queue in memory for any single peer that the specified socket is communicating with.
* A value of zero means no limit.
* If this limit has been reached the socket shall enter an exceptional state and depending on the socket type,
* ØMQ shall take appropriate action such as blocking or dropping sent messages.
* Refer to the individual socket descriptions in zmq_socket(3) for details on the exact action taken for each socket type.
*
* @param value
* @return true if the option was set, otherwise false.
* @see #getRcvHWM()
*/
public boolean setRcvHWM(int value)
{
return setSocketOpt(zmq.ZMQ.ZMQ_RCVHWM, value);
}
/**
* @return the High Water Mark.
* @see #setHWM(int)
*/
@Deprecated
public int getHWM()
{
return -1;
}
/**
* The 'ZMQ_HWM' option shall set the high water mark for the specified 'socket'. The high
* water mark is a hard limit on the maximum number of outstanding messages 0MQ shall queue
* in memory for any single peer that the specified 'socket' is communicating with.
*
* If this limit has been reached the socket shall enter an exceptional state and depending
* on the socket type, 0MQ shall take appropriate action such as blocking or dropping sent
* messages. Refer to the individual socket descriptions in the man page of zmq_socket[3] for
* details on the exact action taken for each socket type.
*
* @param hwm the number of messages to queue.
* @return true if the option was set, otherwise false.
* @deprecated this option uses integer range, use {@link #setHWM(int)} instead
*/
@Deprecated
public boolean setHWM(long hwm)
{
boolean set = true;
set |= setSndHWM(hwm);
set |= setRcvHWM(hwm);
return set;
}
/**
* The 'ZMQ_HWM' option shall set the high water mark for the specified 'socket'. The high
* water mark is a hard limit on the maximum number of outstanding messages 0MQ shall queue
* in memory for any single peer that the specified 'socket' is communicating with.
*
* If this limit has been reached the socket shall enter an exceptional state and depending
* on the socket type, 0MQ shall take appropriate action such as blocking or dropping sent
* messages. Refer to the individual socket descriptions in the man page of zmq_socket[3] for
* details on the exact action taken for each socket type.
*
* @param hwm the number of messages to queue.
* @return true if the option was set, otherwise false
*/
public boolean setHWM(int hwm)
{
boolean set = false;
set |= setSndHWM(hwm);
set |= setRcvHWM(hwm);
return set;
}
/**
* @return the number of messages to swap at most.
* @see #setSwap(long)
*/
@Deprecated
public long getSwap()
{
// not support at zeromq 3
return -1L;
}
/**
* If set, a socket shall keep only one message in its inbound/outbound queue,
* this message being the last message received/the last message to be sent.
* Ignores ZMQ_RCVHWM and ZMQ_SNDHWM options.
* Does not support multi-part messages, in particular,
* only one part of it is kept in the socket internal queue.
*
* @param conflate true to keep only one message, false for standard behaviour.
* @return true if the option was set, otherwise false.
* @see #isConflate()
*/
public boolean setConflate(boolean conflate)
{
return setSocketOpt(zmq.ZMQ.ZMQ_CONFLATE, conflate);
}
/**
* If in conflate mode, a socket shall keep only one message in its inbound/outbound queue,
* this message being the last message received/the last message to be sent.
* Ignores ZMQ_RCVHWM and ZMQ_SNDHWM options.
* Does not support multi-part messages, in particular,
* only one part of it is kept in the socket internal queue.
*
* @return true to keep only one message, false for standard behaviour.
* @see #setConflate(boolean)
*/
public boolean isConflate()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_CONFLATE) != 0;
}
/**
* If in conflate mode, a socket shall keep only one message in its inbound/outbound queue,
* this message being the last message received/the last message to be sent.
* Ignores ZMQ_RCVHWM and ZMQ_SNDHWM options.
* Does not support multi-part messages, in particular,
* only one part of it is kept in the socket internal queue.
*
* @return true to keep only one message, false for standard behaviour.
* @see #setConflate(boolean)
*/
public boolean getConflate()
{
return isConflate();
}
/**
* Get the Swap. The 'ZMQ_SWAP' option shall set the disk offload (swap) size for the
* specified 'socket'. A socket which has 'ZMQ_SWAP' set to a non-zero value may exceed its
* high water mark; in this case outstanding messages shall be offloaded to storage on disk
* rather than held in memory.
*
* @param value The value of 'ZMQ_SWAP' defines the maximum size of the swap space in bytes.
*/
@Deprecated
public boolean setSwap(long value)
{
throw new UnsupportedOperationException();
}
/**
* @return the affinity.
* @see #setAffinity(long)
*/
public long getAffinity()
{
return (Long) base.getSocketOptx(zmq.ZMQ.ZMQ_AFFINITY);
}
/**
* Get the Affinity. The 'ZMQ_AFFINITY' option shall set the I/O thread affinity for newly
* created connections on the specified 'socket'.
*
* Affinity determines which threads from the 0MQ I/O thread pool associated with the
* socket's _context_ shall handle newly created connections. A value of zero specifies no
* affinity, meaning that work shall be distributed fairly among all 0MQ I/O threads in the
* thread pool. For non-zero values, the lowest bit corresponds to thread 1, second lowest
* bit to thread 2 and so on. For example, a value of 3 specifies that subsequent
* connections on 'socket' shall be handled exclusively by I/O threads 1 and 2.
*
* See also in the man page of init[3] for details on allocating the number of I/O threads for a
* specific _context_.
*
* @param value the io_thread affinity.
* @return true if the option was set, otherwise false
*/
public boolean setAffinity(long value)
{
return setSocketOpt(zmq.ZMQ.ZMQ_AFFINITY, value);
}
/**
* @return the Identitiy.
* @see #setIdentity(byte[])
*/
public byte[] getIdentity()
{
return (byte[]) base.getSocketOptx(zmq.ZMQ.ZMQ_IDENTITY);
}
/**
* The 'ZMQ_IDENTITY' option shall set the identity of the specified 'socket'. Socket
* identity determines if existing 0MQ infastructure (_message queues_, _forwarding
* devices_) shall be identified with a specific application and persist across multiple
* runs of the application.
*
* If the socket has no identity, each run of an application is completely separate from
* other runs. However, with identity set the socket shall re-use any existing 0MQ
* infrastructure configured by the previous run(s). Thus the application may receive
* messages that were sent in the meantime, _message queue_ limits shall be shared with
* previous run(s) and so on.
*
* Identity should be at least one byte and at most 255 bytes long. Identities starting with
* binary zero are reserved for use by 0MQ infrastructure.
*
* @param identity
* @return true if the option was set, otherwise false
*/
public boolean setIdentity(byte[] identity)
{
return setSocketOpt(zmq.ZMQ.ZMQ_IDENTITY, identity);
}
/**
* @return the Rate.
* @see #setRate(long)
*/
public long getRate()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_RATE);
}
/**
* The 'ZMQ_RATE' option shall set the maximum send or receive data rate for multicast
* transports such as in the man page of zmq_pgm[7] using the specified 'socket'.
*
* @param value maximum send or receive data rate for multicast, default 100
* @return true if the option was set, otherwise false
*/
public boolean setRate(long value)
{
throw new UnsupportedOperationException();
}
/**
* The ZMQ_RECOVERY_IVL option shall retrieve the recovery interval for multicast transports
* using the specified socket. The recovery interval determines the maximum time in milliseconds
* that a receiver can be absent from a multicast group before unrecoverable data loss will occur.
*
* @return the RecoveryIntervall.
* @see #setRecoveryInterval(long)
*/
public long getRecoveryInterval()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_RECOVERY_IVL);
}
/**
* The 'ZMQ_RECOVERY_IVL' option shall set the recovery interval for multicast transports
* using the specified 'socket'. The recovery interval determines the maximum time in
* seconds that a receiver can be absent from a multicast group before unrecoverable data
* loss will occur.
*
* CAUTION: Exercise care when setting large recovery intervals as the data needed for
* recovery will be held in memory. For example, a 1 minute recovery interval at a data rate
* of 1Gbps requires a 7GB in-memory buffer. {Purpose of this Method}
*
* @param value recovery interval for multicast in milliseconds, default 10000
* @return true if the option was set, otherwise false.
* @see #getRecoveryInterval()
*/
public boolean setRecoveryInterval(long value)
{
throw new UnsupportedOperationException();
}
/**
* The default behavior of REQ sockets is to rely on the ordering of messages
* to match requests and responses and that is usually sufficient.
* When this option is set to true, the REQ socket will prefix outgoing messages
* with an extra frame containing a request id.
* That means the full message is (request id, identity, 0, user frames…).
* The REQ socket will discard all incoming messages that don't begin with these two frames.
* See also ZMQ_REQ_RELAXED.
*
* @param correlate Whether to enable outgoing request ids.
* @return true if the option was set, otherwise false
* @see #getReqCorrelate()
*/
public boolean setReqCorrelate(boolean correlate)
{
return setSocketOpt(zmq.ZMQ.ZMQ_REQ_CORRELATE, correlate);
}
/**
* The default behavior of REQ sockets is to rely on the ordering of messages
* to match requests and responses and that is usually sufficient.
* When this option is set to true, the REQ socket will prefix outgoing messages
* with an extra frame containing a request id.
* That means the full message is (request id, identity, 0, user frames…).
* The REQ socket will discard all incoming messages that don't begin with these two frames.
*
* @return state of the ZMQ_REQ_CORRELATE option.
* @see #setReqCorrelate(boolean)
*/
@Deprecated
public boolean getReqCorrelate()
{
throw new UnsupportedOperationException();
}
/**
* By default, a REQ socket does not allow initiating a new request with zmq_send(3)
* until the reply to the previous one has been received.
* When set to true, sending another message is allowed and has the effect of disconnecting
* the underlying connection to the peer from which the reply was expected,
* triggering a reconnection attempt on transports that support it.
* The request-reply state machine is reset and a new request is sent to the next available peer.
* If set to true, also enable ZMQ_REQ_CORRELATE to ensure correct matching of requests and replies.
* Otherwise a late reply to an aborted request can be reported as the reply to the superseding request.
*
* @param relaxed
* @return true if the option was set, otherwise false
* @see #getReqRelaxed()
*/
public boolean setReqRelaxed(boolean relaxed)
{
return setSocketOpt(zmq.ZMQ.ZMQ_REQ_RELAXED, relaxed);
}
/**
* By default, a REQ socket does not allow initiating a new request with zmq_send(3)
* until the reply to the previous one has been received.
* When set to true, sending another message is allowed and has the effect of disconnecting
* the underlying connection to the peer from which the reply was expected,
* triggering a reconnection attempt on transports that support it.
* The request-reply state machine is reset and a new request is sent to the next available peer.
* If set to true, also enable ZMQ_REQ_CORRELATE to ensure correct matching of requests and replies.
* Otherwise a late reply to an aborted request can be reported as the reply to the superseding request.
*
* @return state of the ZMQ_REQ_RELAXED option.
* @see #setReqRelaxed(boolean)
*/
@Deprecated
public boolean getReqRelaxed()
{
throw new UnsupportedOperationException();
}
/**
* @return the Multicast Loop.
* @see #setMulticastLoop(boolean)
*/
@Deprecated
public boolean hasMulticastLoop()
{
return false;
}
/**
* The 'ZMQ_MCAST_LOOP' option shall control whether data sent via multicast transports
* using the specified 'socket' can also be received by the sending host via loopback. A
* value of zero disables the loopback functionality, while the default value of 1 enables
* the loopback functionality. Leaving multicast loopback enabled when it is not required
* can have a negative impact on performance. Where possible, disable 'ZMQ_MCAST_LOOP' in
* production environments.
*
* @param multicastLoop
*/
@Deprecated
public boolean setMulticastLoop(boolean multicastLoop)
{
throw new UnsupportedOperationException();
}
/**
* @return the Multicast Hops.
* @see #setMulticastHops(long)
*/
public long getMulticastHops()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_MULTICAST_HOPS);
}
/**
* Sets the time-to-live field in every multicast packet sent from this socket.
* The default is 1 which means that the multicast packets don't leave the local
* network.
*
* @param value time-to-live field in every multicast packet, default 1
*/
public boolean setMulticastHops(long value)
{
throw new UnsupportedOperationException();
}
/**
* Retrieve the timeout for recv operation on the socket.
* If the value is 0, recv will return immediately,
* with null if there is no message to receive.
* If the value is -1, it will block until a message is available.
* For all other values, it will wait for a message for that amount of time
* before returning with a null and an EAGAIN error.
*
* @return the Receive Timeout in milliseconds.
* @see #setReceiveTimeOut(int)
*/
public int getReceiveTimeOut()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_RCVTIMEO);
}
/**
* Sets the timeout for receive operation on the socket. If the value is 0, recv
* will return immediately, with null if there is no message to receive.
* If the value is -1, it will block until a message is available. For all other
* values, it will wait for a message for that amount of time before returning with
* a null and an EAGAIN error.
*
* @param value Timeout for receive operation in milliseconds. Default -1 (infinite)
* @return true if the option was set, otherwise false.
* @see #getReceiveTimeOut()
*/
public boolean setReceiveTimeOut(int value)
{
return setSocketOpt(zmq.ZMQ.ZMQ_RCVTIMEO, value);
}
/**
* Retrieve the timeout for send operation on the socket.
* If the value is 0, send will return immediately, with a false and an EAGAIN error if the message cannot be sent.
* If the value is -1, it will block until the message is sent.
* For all other values, it will try to send the message for that amount of time before returning with false and an EAGAIN error.
*
* @return the Send Timeout in milliseconds.
* @see #setSendTimeOut(int)
*/
public int getSendTimeOut()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_SNDTIMEO);
}
/**
* Sets the timeout for send operation on the socket. If the value is 0, send
* will return immediately, with a false if the message cannot be sent.
* If the value is -1, it will block until the message is sent. For all other
* values, it will try to send the message for that amount of time before
* returning with false and an EAGAIN error.
*
* @param value Timeout for send operation in milliseconds. Default -1 (infinite)
* @return true if the option was set, otherwise false.
* @see #getSendTimeOut()
*/
public boolean setSendTimeOut(int value)
{
return setSocketOpt(zmq.ZMQ.ZMQ_SNDTIMEO, value);
}
/**
* Override SO_KEEPALIVE socket option (where supported by OS) to enable keep-alive packets for a socket
* connection. Possible values are -1, 0, 1. The default value -1 will skip all overrides and do the OS default.
*
* @param value The value of 'ZMQ_TCP_KEEPALIVE' to turn TCP keepalives on (1) or off (0).
* @return true if the option was set, otherwise false.
*/
@Deprecated
public boolean setTCPKeepAlive(long value)
{
return setTCPKeepAlive(Long.valueOf(value).intValue());
}
/**
* @return the keep alive setting.
* @see #setTCPKeepAlive(long)
*/
@Deprecated
public long getTCPKeepAliveSetting()
{
return getTCPKeepAlive();
}
/**
* Override TCP_KEEPCNT socket option (where supported by OS). The default value -1 will skip all overrides and
* do the OS default.
*
* @param value The value of 'ZMQ_TCP_KEEPALIVE_CNT' defines the number of keepalives before death.
* @return true if the option was set, otherwise false.
*/
public boolean setTCPKeepAliveCount(long value)
{
return setSocketOpt(zmq.ZMQ.ZMQ_TCP_KEEPALIVE_CNT, Long.valueOf(value).intValue());
}
/**
* @return the keep alive count.
* @see #setTCPKeepAliveCount(long)
*/
public long getTCPKeepAliveCount()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_TCP_KEEPALIVE_CNT);
}
/**
* Override TCP_KEEPINTVL socket option (where supported by OS). The default value -1 will skip all overrides
* and do the OS default.
*
* @param value The value of 'ZMQ_TCP_KEEPALIVE_INTVL' defines the interval between keepalives. Unit is OS
* dependent.
* @return true if the option was set, otherwise false.
*/
public boolean setTCPKeepAliveInterval(long value)
{
return setSocketOpt(zmq.ZMQ.ZMQ_TCP_KEEPALIVE_INTVL, Long.valueOf(value).intValue());
}
/**
* @return the keep alive interval.
* @see #setTCPKeepAliveInterval(long)
*/
public long getTCPKeepAliveInterval()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_TCP_KEEPALIVE_INTVL);
}
/**
* Override TCP_KEEPCNT (or TCP_KEEPALIVE on some OS) socket option (where supported by OS). The default value
* -1 will skip all overrides and do the OS default.
*
* @param value The value of 'ZMQ_TCP_KEEPALIVE_IDLE' defines the interval between the last data packet sent
* over the socket and the first keepalive probe. Unit is OS dependent.
* @return true if the option was set, otherwise false
*/
public boolean setTCPKeepAliveIdle(long value)
{
return setSocketOpt(zmq.ZMQ.ZMQ_TCP_KEEPALIVE_IDLE, Long.valueOf(value).intValue());
}
/**
* @return the keep alive idle value.
* @see #setTCPKeepAliveIdle(long)
*/
public long getTCPKeepAliveIdle()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_TCP_KEEPALIVE_IDLE);
}
/**
* The ZMQ_SNDBUF option shall retrieve the underlying kernel transmit buffer size for the specified socket.
* For details refer to your operating system documentation for the SO_SNDBUF socket option.
*
* @return the kernel send buffer size.
* @see #setSendBufferSize(int)
*/
public int getSendBufferSize()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_SNDBUF);
}
/**
* The 'ZMQ_SNDBUF' option shall set the underlying kernel transmit buffer size for the
* 'socket' to the specified size in bytes. A value of zero means leave the OS default
* unchanged. For details please refer to your operating system documentation for the
* 'SO_SNDBUF' socket option.
*
* @param value underlying kernel transmit buffer size for the 'socket' in bytes
* A value of zero means leave the OS default unchanged.
* @return true if the option was set, otherwise false
* @see #getSendBufferSize()
* @deprecated this option uses integer range, use {@link #setSendBufferSize(int)} instead
*/
@Deprecated
public boolean setSendBufferSize(long value)
{
return setSendBufferSize(Long.valueOf(value).intValue());
}
/**
* The 'ZMQ_SNDBUF' option shall set the underlying kernel transmit buffer size for the
* 'socket' to the specified size in bytes. A value of zero means leave the OS default
* unchanged. For details please refer to your operating system documentation for the
* 'SO_SNDBUF' socket option.
*
* @param value underlying kernel transmit buffer size for the 'socket' in bytes
* A value of zero means leave the OS default unchanged.
* @return true if the option was set, otherwise false
* @see #getSendBufferSize()
*/
public boolean setSendBufferSize(int value)
{
return setSocketOpt(zmq.ZMQ.ZMQ_SNDBUF, value);
}
/**
* The ZMQ_RCVBUF option shall retrieve the underlying kernel receive buffer size for the specified socket.
* For details refer to your operating system documentation for the SO_RCVBUF socket option.
*
* @return the kernel receive buffer size.
* @see #setReceiveBufferSize(int)
*/
public int getReceiveBufferSize()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_RCVBUF);
}
/**
* The 'ZMQ_RCVBUF' option shall set the underlying kernel receive buffer size for the
* 'socket' to the specified size in bytes.
* For details refer to your operating system documentation for the 'SO_RCVBUF'
* socket option.
*
* @param value Underlying kernel receive buffer size for the 'socket' in bytes.
* A value of zero means leave the OS default unchanged.
* @return true if the option was set, otherwise false
* @see #getReceiveBufferSize()
* @deprecated this option uses integer range, use {@link #setReceiveBufferSize(int)} instead
*/
@Deprecated
public boolean setReceiveBufferSize(long value)
{
return setReceiveBufferSize(Long.valueOf(value).intValue());
}
/**
* The 'ZMQ_RCVBUF' option shall set the underlying kernel receive buffer size for the
* 'socket' to the specified size in bytes.
* For details refer to your operating system documentation for the 'SO_RCVBUF'
* socket option.
*
* @param value Underlying kernel receive buffer size for the 'socket' in bytes.
* A value of zero means leave the OS default unchanged.
* @return true if the option was set, otherwise false
* @see #getReceiveBufferSize()
*/
public boolean setReceiveBufferSize(int value)
{
return setSocketOpt(zmq.ZMQ.ZMQ_RCVBUF, value);
}
/**
* The 'ZMQ_RCVMORE' option shall return a boolean value indicating if the multi-part
* message currently being read from the specified 'socket' has more message parts to
* follow. If there are no message parts to follow or if the message currently being read is
* not a multi-part message a value of zero shall be returned. Otherwise, a value of 1 shall
* be returned.
*
* @return true if there are more messages to receive.
*/
public boolean hasReceiveMore()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_RCVMORE) == 1;
}
/**
* The 'ZMQ_FD' option shall retrieve file descriptor associated with the 0MQ
* socket. The descriptor can be used to integrate 0MQ socket into an existing
* event loop. It should never be used for anything else than polling -- such as
* reading or writing. The descriptor signals edge-triggered IN event when
* something has happened within the 0MQ socket. It does not necessarily mean that
* the messages can be read or written. Check ZMQ_EVENTS option to find out whether
* the 0MQ socket is readable or writeable.
*
* @return the underlying file descriptor.
*/
public SelectableChannel getFD()
{
return (SelectableChannel) base.getSocketOptx(zmq.ZMQ.ZMQ_FD);
}
/**
* The 'ZMQ_EVENTS' option shall retrieve event flags for the specified socket.
* If a message can be read from the socket ZMQ_POLLIN flag is set. If message can
* be written to the socket ZMQ_POLLOUT flag is set.
*
* @return the mask of outstanding events.
*/
public int getEvents()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_EVENTS);
}
/**
* The 'ZMQ_SUBSCRIBE' option shall establish a new message filter on a 'ZMQ_SUB' socket.
* Newly created 'ZMQ_SUB' sockets shall filter out all incoming messages, therefore you
* should call this option to establish an initial message filter.
*
* An empty 'option_value' of length zero shall subscribe to all incoming messages. A
* non-empty 'option_value' shall subscribe to all messages beginning with the specified
* prefix. Mutiple filters may be attached to a single 'ZMQ_SUB' socket, in which case a
* message shall be accepted if it matches at least one filter.
*
* @param topic
* @return true if the option was set, otherwise false
*/
public boolean subscribe(byte[] topic)
{
return setSocketOpt(zmq.ZMQ.ZMQ_SUBSCRIBE, topic);
}
/**
* The 'ZMQ_SUBSCRIBE' option shall establish a new message filter on a 'ZMQ_SUB' socket.
* Newly created 'ZMQ_SUB' sockets shall filter out all incoming messages, therefore you
* should call this option to establish an initial message filter.
*
* An empty 'option_value' of length zero shall subscribe to all incoming messages. A
* non-empty 'option_value' shall subscribe to all messages beginning with the specified
* prefix. Mutiple filters may be attached to a single 'ZMQ_SUB' socket, in which case a
* message shall be accepted if it matches at least one filter.
*
* @param topic
* @return true if the option was set, otherwise false
*/
public boolean subscribe(String topic)
{
return setSocketOpt(zmq.ZMQ.ZMQ_SUBSCRIBE, topic);
}
/**
* The 'ZMQ_UNSUBSCRIBE' option shall remove an existing message filter on a 'ZMQ_SUB'
* socket. The filter specified must match an existing filter previously established with
* the 'ZMQ_SUBSCRIBE' option. If the socket has several instances of the same filter
* attached the 'ZMQ_UNSUBSCRIBE' option shall remove only one instance, leaving the rest in
* place and functional.
*
* @param topic
* @return true if the option was set, otherwise false
*/
public boolean unsubscribe(byte[] topic)
{
return setSocketOpt(zmq.ZMQ.ZMQ_UNSUBSCRIBE, topic);
}
/**
* The 'ZMQ_UNSUBSCRIBE' option shall remove an existing message filter on a 'ZMQ_SUB'
* socket. The filter specified must match an existing filter previously established with
* the 'ZMQ_SUBSCRIBE' option. If the socket has several instances of the same filter
* attached the 'ZMQ_UNSUBSCRIBE' option shall remove only one instance, leaving the rest in
* place and functional.
*
* @param topic
* @return true if the option was set, otherwise false
*/
public boolean unsubscribe(String topic)
{
return setSocketOpt(zmq.ZMQ.ZMQ_UNSUBSCRIBE, topic);
}
/**
* Joins a group.
* Opposite action is {@link Socket#leave(String)}
* @param group the name of the group to join. Limited to 16 characters.
* @return true if the group was no already joined, otherwise false.
*/
public boolean join(String group)
{
assert ("DISH".equals(base.typeString())) : "Only DISH sockets can join a group";
return base.join(group);
}
/**
* Leaves a group.
* Opposite action is {@link Socket#join(String)}
* @param group the name of the group to leave. Limited to 16 characters.
* @return false if the group was not joined before, otherwise true.
*/
public boolean leave(String group)
{
assert ("DISH".equals(base.typeString())) : "Only DISH sockets can leave a group";
return base.leave(group);
}
/**
* Set custom Encoder
*
* @param cls
* @return true if the option was set, otherwise false
*/
@Deprecated
public boolean setEncoder(Class extends IEncoder> cls)
{
return setSocketOpt(zmq.ZMQ.ZMQ_ENCODER, cls);
}
/**
* Set custom Decoder
*
* @param cls
* @return true if the option was set, otherwise false
*/
@Deprecated
public boolean setDecoder(Class extends IDecoder> cls)
{
return setSocketOpt(zmq.ZMQ.ZMQ_DECODER, cls);
}
/**
* Sets the limit threshold where messages of a given size will be allocated using Direct ByteBuffer.
* It means that after this limit, there will be a slight penalty cost at the creation,
* but the subsequent operations will be faster.
* Set to 0 or negative to disable the threshold mechanism.
*
* @param threshold the threshold to set for the size limit of messages. 0 or negative to disable this system.
* @return true if the option was set, otherwise false.
*/
public boolean setMsgAllocationHeapThreshold(int threshold)
{
return setSocketOpt(zmq.ZMQ.ZMQ_MSG_ALLOCATION_HEAP_THRESHOLD, threshold);
}
/**
* Gets the limit threshold where messages of a given size will be allocated using Direct ByteBuffer.
* It means that after this limit, there will be a slight penalty cost at the creation,
* but the subsequent operations will be faster.
*
* @return the threshold
*/
public int getMsgAllocationHeapThreshold()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_MSG_ALLOCATION_HEAP_THRESHOLD);
}
/**
* Sets a custom message allocator.
*
* @param allocator the custom allocator.
* @return true if the option was set, otherwise false.
*/
public boolean setMsgAllocator(MsgAllocator allocator)
{
return setSocketOpt(zmq.ZMQ.ZMQ_MSG_ALLOCATOR, allocator);
}
/**
* Set a custom {@link java.nio.channels.spi.SelectorProvider} chooser.
*
* @param chooser the custom chooser.
* @return true if the option was set, otherwise false.
*/
public boolean setSelectorChooser(SelectorProviderChooser chooser)
{
return base.setSocketOpt(zmq.ZMQ.ZMQ_SELECTOR_PROVIDERCHOOSER, chooser);
}
/**
* Return the custom {@link java.nio.channels.spi.SelectorProvider} chooser.
*
* @return the {@link java.nio.channels.spi.SelectorProvider} chooser.
*/
public SelectorProviderChooser getSelectorProviderChooser()
{
return (SelectorProviderChooser) base.getSocketOptx(zmq.ZMQ.ZMQ_SELECTOR_PROVIDERCHOOSER);
}
/**
* The ZMQ_CONNECT_RID option sets the peer id of the next host connected via the connect() call,
* and immediately readies that connection for data transfer with the named id.
* This option applies only to the first subsequent call to connect(),
* calls thereafter use default connection behavior.
* Typical use is to set this socket option ahead of each connect() attempt to a new host.
* Each connection MUST be assigned a unique name. Assigning a name that is already in use is not allowed.
* Useful when connecting ROUTER to ROUTER, or STREAM to STREAM, as it allows for immediate sending to peers.
* Outbound id framing requirements for ROUTER and STREAM sockets apply.
* The peer id should be from 1 to 255 bytes long and MAY NOT start with binary zero.
*
* @param rid the peer id of the next host.
* @return true if the option was set, otherwise false.
*/
public boolean setConnectRid(String rid)
{
return setSocketOpt(zmq.ZMQ.ZMQ_CONNECT_RID, rid);
}
/**
* The ZMQ_CONNECT_RID option sets the peer id of the next host connected via the connect() call,
* and immediately readies that connection for data transfer with the named id.
* This option applies only to the first subsequent call to connect(),
* calls thereafter use default connection behavior.
* Typical use is to set this socket option ahead of each connect() attempt to a new host.
* Each connection MUST be assigned a unique name. Assigning a name that is already in use is not allowed.
* Useful when connecting ROUTER to ROUTER, or STREAM to STREAM, as it allows for immediate sending to peers.
* Outbound id framing requirements for ROUTER and STREAM sockets apply.
* The peer id should be from 1 to 255 bytes long and MAY NOT start with binary zero.
*
* @param rid the peer id of the next host.
* @return true if the option was set, otherwise false.
*/
public boolean setConnectRid(byte[] rid)
{
return setSocketOpt(zmq.ZMQ.ZMQ_CONNECT_RID, rid);
}
/**
* Sets the raw mode on the ROUTER, when set to true.
* When the ROUTER socket is in raw mode, and when using the tcp:// transport,
* it will read and write TCP data without ØMQ framing.
* This lets ØMQ applications talk to non-ØMQ applications.
* When using raw mode, you cannot set explicit identities,
* and the ZMQ_SNDMORE flag is ignored when sending data messages.
* In raw mode you can close a specific connection by sending it a zero-length message (following the identity frame).
*
* @param raw true to set the raw mode on the ROUTER.
* @return true if the option was set, otherwise false.
*/
public boolean setRouterRaw(boolean raw)
{
return setSocketOpt(zmq.ZMQ.ZMQ_ROUTER_RAW, raw);
}
/**
* When set to true, the socket will automatically send
* an empty message when a new connection is made or accepted.
* You may set this on REQ, DEALER, or ROUTER sockets connected to a ROUTER socket.
* The application must filter such empty messages.
* The ZMQ_PROBE_ROUTER option in effect provides the ROUTER application with an event signaling the arrival of a new peer.
*
* @param probe true to send automatically an empty message when a new connection is made or accepted.
* @return true if the option was set, otherwise false.
*/
public boolean setProbeRouter(boolean probe)
{
return setSocketOpt(zmq.ZMQ.ZMQ_PROBE_ROUTER, probe);
}
/**
* Sets the ROUTER socket behavior when an unroutable message is encountered.
* A value of false is the default and discards the message silently
* when it cannot be routed or the peers SNDHWM is reached.
* A value of true returns an EHOSTUNREACH error code if the message cannot be routed
* or EAGAIN error code if the SNDHWM is reached and ZMQ_DONTWAIT was used.
* Without ZMQ_DONTWAIT it will block until the SNDTIMEO is reached or a spot in the send queue opens up.
*
* @param mandatory A value of false is the default and discards the message silently when it cannot be routed.
* A value of true returns an EHOSTUNREACH error code if the message cannot be routed.
* @return true if the option was set, otherwise false.
*/
public boolean setRouterMandatory(boolean mandatory)
{
return setSocketOpt(zmq.ZMQ.ZMQ_ROUTER_MANDATORY, mandatory);
}
/**
* If two clients use the same identity when connecting to a ROUTER,
* the results shall depend on the ZMQ_ROUTER_HANDOVER option setting.
* If that is not set (or set to the default of false),
* the ROUTER socket shall reject clients trying to connect with an already-used identity.
* If that option is set to true, the ROUTER socket shall hand-over the connection to the new client and disconnect the existing one.
*
* @param handover A value of false, (default) the ROUTER socket shall reject clients trying to connect with an already-used identity
* A value of true, the ROUTER socket shall hand-over the connection to the new client and disconnect the existing one
* @return true if the option was set, otherwise false.
*/
public boolean setRouterHandover(boolean handover)
{
return setSocketOpt(zmq.ZMQ.ZMQ_ROUTER_HANDOVER, handover);
}
/**
* Sets the XPUB socket behavior on new subscriptions and unsubscriptions.
*
* @param verbose A value of false is the default and passes only new subscription messages to upstream.
* A value of true passes all subscription messages upstream.
* @return true if the option was set, otherwise false.
*/
public boolean setXpubVerbose(boolean verbose)
{
return setSocketOpt(zmq.ZMQ.ZMQ_XPUB_VERBOSE, verbose);
}
/**
* Sets the XPUB socket behaviour to return error EAGAIN if SENDHWM is reached and the message could not be send.
* A value of false is the default and drops the message silently when the peers SNDHWM is reached.
* A value of true returns an EAGAIN error code if the SNDHWM is reached and ZMQ_DONTWAIT was used.
*
* @param noDrop
* @return true if the option was set, otherwise false.
*/
public boolean setXpubNoDrop(boolean noDrop)
{
return setSocketOpt(zmq.ZMQ.ZMQ_XPUB_NODROP, noDrop);
}
public boolean setXpubManual(boolean manual)
{
return setSocketOpt(zmq.ZMQ.ZMQ_XPUB_MANUAL, manual);
}
public boolean setXpubVerboser(boolean verboser)
{
return setSocketOpt(zmq.ZMQ.ZMQ_XPUB_VERBOSER, verboser);
}
/**
* @return the IPV4ONLY
* @see #setIPv4Only (boolean)
* @deprecated use {@link #isIPv6()} instead (inverted logic: ipv4 = true <==> ipv6 = false)
*/
@Deprecated
public boolean getIPv4Only()
{
return !isIPv6();
}
/**
* Retrieve the IPv6 option for the socket.
* A value of true means IPv6 is enabled on the socket,
* while false means the socket will use only IPv4.
* When IPv6 is enabled the socket will connect to,
* or accept connections from, both IPv4 and IPv6 hosts.
*
* @return the IPV6 configuration.
* @see #setIPv6 (boolean)
*/
public boolean isIPv6()
{
return (Boolean) base.getSocketOptx(zmq.ZMQ.ZMQ_IPV6);
}
/**
* Retrieve the IPv6 option for the socket.
* A value of true means IPv6 is enabled on the socket,
* while false means the socket will use only IPv4.
* When IPv6 is enabled the socket will connect to,
* or accept connections from, both IPv4 and IPv6 hosts.
*
* @return the IPV6 configuration.
* @see #setIPv6 (boolean)
*/
public boolean getIPv6()
{
return isIPv6();
}
/**
* The 'ZMQ_IPV4ONLY' option shall set the underlying native socket type.
* An IPv6 socket lets applications connect to and accept connections from both IPv4 and IPv6 hosts.
*
* @param v4only A value of true will use IPv4 sockets, while the value of false will use IPv6 sockets
* @return true if the option was set, otherwise false
* @deprecated use {@link #setIPv6(boolean)} instead (inverted logic: ipv4 = true <==> ipv6 = false)
*/
@Deprecated
public boolean setIPv4Only(boolean v4only)
{
return setIPv6(!v4only);
}
/**
*
Set the IPv6 option for the socket.
* A value of true means IPv6 is enabled on the socket, while false means the socket will use only IPv4.
* When IPv6 is enabled the socket will connect to, or accept connections from, both IPv4 and IPv6 hosts.
* The default value is false, unless the following system properties are set:
*
* java.net.preferIPv4Stack=false
* java.net.preferIPv6Addresses=true
*
*
* @param v6 A value of true will use IPv6 sockets, while the value of false will use IPv4 sockets only
* @return true if the option was set, otherwise false
* @see #isIPv6()
*/
public boolean setIPv6(boolean v6)
{
return setSocketOpt(zmq.ZMQ.ZMQ_IPV6, v6);
}
/**
* @return the keep alive setting.
* @see #setTCPKeepAlive(int)
*/
public int getTCPKeepAlive()
{
return base.getSocketOpt(zmq.ZMQ.ZMQ_TCP_KEEPALIVE);
}
/**
* Override SO_KEEPALIVE socket option (where supported by OS) to enable keep-alive packets for a socket
* connection. Possible values are -1, 0, 1. The default value -1 will skip all overrides and do the OS default.
*
* @param optVal The value of 'ZMQ_TCP_KEEPALIVE' to turn TCP keepalives on (1) or off (0).
* @return true if the option was set, otherwise false
*/
public boolean setTCPKeepAlive(int optVal)
{
return setSocketOpt(zmq.ZMQ.ZMQ_TCP_KEEPALIVE, optVal);
}
/**
* @see #setDelayAttachOnConnect(boolean)
* @deprecated use {@link #setImmediate(boolean)} instead (inverted logic: immediate = true <==> delay attach on connect = false)
*/
@Deprecated
public boolean getDelayAttachOnConnect()
{
return !isImmediate();
}
/**
* Accept messages only when connections are made
*
* If set to true, will delay the attachment of a pipe on connect until the underlying connection
* has completed. This will cause the socket to block if there are no other connections, but will
* prevent queues from filling on pipes awaiting connection
*
* @param value The value of 'ZMQ_DELAY_ATTACH_ON_CONNECT'. Default false.
* @return true if the option was set
* @deprecated use {@link #setImmediate(boolean)} instead (warning, the boolean is inverted)
*/
@Deprecated
public boolean setDelayAttachOnConnect(boolean value)
{
return setImmediate(!value);
}
/**
* Retrieve the state of the attach on connect value.
* If false, will delay the attachment of a pipe on connect until the underlying connection has completed.
* This will cause the socket to block if there are no other connections, but will prevent queues from filling on pipes awaiting connection.
*
* @see #setImmediate(boolean)
*/
public boolean isImmediate()
{
return (boolean) base.getSocketOptx(zmq.ZMQ.ZMQ_IMMEDIATE);
}
/**
* Retrieve the state of the attach on connect value.
* If false, will delay the attachment of a pipe on connect until the underlying connection has completed.
* This will cause the socket to block if there are no other connections, but will prevent queues from filling on pipes awaiting connection.
*
* @see #setImmediate(boolean)
*/
public boolean getImmediate()
{
return isImmediate();
}
/**
* Accept messages immediately or only when connections are made
*
* By default queues will fill on outgoing connections even if the connection has not completed.
* This can lead to "lost" messages on sockets with round-robin routing (REQ, PUSH, DEALER).
* If this option is set to false, messages shall be queued only to completed connections.
* This will cause the socket to block if there are no other connections,
* but will prevent queues from filling on pipes awaiting connection.
*
* @param value The value of 'ZMQ_IMMEDIATE'. Default true.
* @return true if the option was set, otherwise false.
* @see #isImmediate()
*/
public boolean setImmediate(boolean value)
{
return setSocketOpt(zmq.ZMQ.ZMQ_IMMEDIATE, value);
}
/**
* Sets the SOCKS5 proxy address that shall be used by the socket for the TCP connection(s).
* Does not support SOCKS5 authentication.
* If the endpoints are domain names instead of addresses they shall not be resolved
* and they shall be forwarded unchanged to the SOCKS proxy service
* in the client connection request message (address type 0x03 domain name).
*
* @param proxy
* @return true if the option was set, otherwise false.
* @see #getSocksProxy()
*/
public boolean setSocksProxy(String proxy)
{
return setSocketOpt(zmq.ZMQ.ZMQ_SOCKS_PROXY, proxy);
}
/**
* Sets the SOCKS5 proxy address that shall be used by the socket for the TCP connection(s).
* Does not support SOCKS5 authentication.
* If the endpoints are domain names instead of addresses they shall not be resolved
* and they shall be forwarded unchanged to the SOCKS proxy service
* in the client connection request message (address type 0x03 domain name).
*
* @param proxy
* @return true if the option was set, otherwise false.
* @see #getSocksProxy()
*/
public boolean setSocksProxy(byte[] proxy)
{
return setSocketOpt(zmq.ZMQ.ZMQ_SOCKS_PROXY, proxy);
}
/**
* The ZMQ_SOCKS_PROXY option shall retrieve the SOCKS5 proxy address in string format.
* The returned value MAY be empty.
*
* @return the SOCKS5 proxy address in string format
* @see #setSocksProxy(byte[])
*/
public String getSocksProxy()
{
return (String) base.getSocketOptx(zmq.ZMQ.ZMQ_SOCKS_PROXY);
}
/**
* The ZMQ_LAST_ENDPOINT option shall retrieve the last endpoint bound for TCP and IPC transports.
* The returned value will be a string in the form of a ZMQ DSN.
* Note that if the TCP host is INADDR_ANY, indicated by a *, then the returned address will be 0.0.0.0 (for IPv4).
*/
public String getLastEndpoint()
{
return (String) base.getSocketOptx(zmq.ZMQ.ZMQ_LAST_ENDPOINT);
}
/**
* Sets the domain for ZAP (ZMQ RFC 27) authentication.
* For NULL security (the default on all tcp:// connections),
* ZAP authentication only happens if you set a non-empty domain.
* For PLAIN and CURVE security, ZAP requests are always made, if there is a ZAP handler present.
* See http://rfc.zeromq.org/spec:27 for more details.
*
* @param domain the domain of ZAP authentication
* @return true if the option was set
* @see #getZapDomain()
*/
public boolean setZapDomain(String domain)
{
return setSocketOpt(zmq.ZMQ.ZMQ_ZAP_DOMAIN, domain);
}
/**
* Sets the domain for ZAP (ZMQ RFC 27) authentication.
* For NULL security (the default on all tcp:// connections),
* ZAP authentication only happens if you set a non-empty domain.
* For PLAIN and CURVE security, ZAP requests are always made, if there is a ZAP handler present.
* See http://rfc.zeromq.org/spec:27 for more details.
*
* @param domain the domain of ZAP authentication
* @return true if the option was set
* @see #getZapDomain()
*/
public boolean setZapDomain(byte[] domain)
{
return setSocketOpt(zmq.ZMQ.ZMQ_ZAP_DOMAIN, domain);
}
/**
* The ZMQ_ZAP_DOMAIN option shall retrieve the last ZAP domain set for the socket.
* The returned value MAY be empty.
*
* @return the domain of ZAP authentication
* @see #setZapDomain(String)
*/
public String getZapDomain()
{
return (String) base.getSocketOptx(zmq.ZMQ.ZMQ_ZAP_DOMAIN);
}
/**
* Sets the domain for ZAP (ZMQ RFC 27) authentication.
* For NULL security (the default on all tcp:// connections),
* ZAP authentication only happens if you set a non-empty domain.
* For PLAIN and CURVE security, ZAP requests are always made, if there is a ZAP handler present.
* See http://rfc.zeromq.org/spec:27 for more details.
*
* @param domain the domain of ZAP authentication
* @return true if the option was set
* @see #getZapDomain()
*/
public boolean setZAPDomain(String domain)
{
return setZapDomain(domain);
}
/**
* Sets the domain for ZAP (ZMQ RFC 27) authentication.
* For NULL security (the default on all tcp:// connections),
* ZAP authentication only happens if you set a non-empty domain.
* For PLAIN and CURVE security, ZAP requests are always made, if there is a ZAP handler present.
* See http://rfc.zeromq.org/spec:27 for more details.
*
* @param domain the domain of ZAP authentication
* @return true if the option was set
* @see #getZapDomain()
*/
public boolean setZAPDomain(byte[] domain)
{
return setZapDomain(domain);
}
/**
* The ZMQ_ZAP_DOMAIN option shall retrieve the last ZAP domain set for the socket.
* The returned value MAY be empty.
*
* @return the domain of ZAP authentication
* @see #setZapDomain(String)
*/
public String getZAPDomain()
{
return getZapDomain();
}
/**
* The ZMQ_SELFADDR_PROPERTY_NAME option shall retrieve the metadata record used to store the self address.
* The returned value MAY be null or empty.
*
* @return the meta record name
* @see #setSelfAddressPropertyName(String)
*/
public String getSelfAddressPropertyName()
{
return (String) base.getSocketOptx(zmq.ZMQ.ZMQ_SELFADDR_PROPERTY_NAME);
}
/**
* Sets the field name where the self address will be stored.
* If set to null or empty string, it will not be stored
*
* @param recordName the name of the field
* @return true if the option was set
* @see #getSelfAddressPropertyName()
*/
public boolean setSelfAddressPropertyName(String recordName)
{
return setSocketOpt(zmq.ZMQ.ZMQ_SELFADDR_PROPERTY_NAME, recordName);
}
/**
* Defines whether the socket will act as server for PLAIN security, see zmq_plain(7).
* A value of true means the socket will act as PLAIN server.
* A value of false means the socket will not act as PLAIN server,
* and its security role then depends on other option settings.
* Setting this to false shall reset the socket security to NULL.
*
* @param server true if the role of the socket should be server for PLAIN security.
* @return true if the option was set, otherwise false.
* @see #isAsServerPlain()
* @deprecated the naming is inconsistent with jzmq, please use {@link #setPlainServer(boolean)} instead
*/
@Deprecated
public boolean setAsServerPlain(boolean server)
{
return setPlainServer(server);
}
/**
* Defines whether the socket will act as server for PLAIN security, see zmq_plain(7).
* A value of true means the socket will act as PLAIN server.
* A value of false means the socket will not act as PLAIN server,
* and its security role then depends on other option settings.
* Setting this to false shall reset the socket security to NULL.
*
* @param server true if the role of the socket should be server for PLAIN security.
* @return true if the option was set, otherwise false.
* @see #isAsServerPlain()
*/
public boolean setPlainServer(boolean server)
{
return setSocketOpt(zmq.ZMQ.ZMQ_PLAIN_SERVER, server);
}
/**
* Returns the ZMQ_PLAIN_SERVER option, if any, previously set on the socket.
*
* @return true if the role of the socket should be server for the PLAIN mechanism.
* @see #setAsServerPlain(boolean)
* @deprecated the naming is inconsistent with jzmq, please use {@link #getPlainServer()} instead
*/
@Deprecated
public boolean isAsServerPlain()
{
return getPlainServer();
}
/**
* Returns the ZMQ_PLAIN_SERVER option, if any, previously set on the socket.
*
* @return true if the role of the socket should be server for the PLAIN mechanism.
* @see #setAsServerPlain(boolean)
* @deprecated the naming is inconsistent with jzmq, please use {@link #getPlainServer()} instead
*/
@Deprecated
public boolean getAsServerPlain()
{
return getPlainServer();
}
/**
* Returns the ZMQ_PLAIN_SERVER option, if any, previously set on the socket.
*
* @return true if the role of the socket should be server for the PLAIN mechanism.
* @see #setAsServerPlain(boolean)
*/
public boolean getPlainServer()
{
return (Boolean) base.getSocketOptx(zmq.ZMQ.ZMQ_PLAIN_SERVER);
}
/**
* Sets the username for outgoing connections over TCP or IPC.
* If you set this to a non-null value, the security mechanism used for connections shall be PLAIN, see zmq_plain(7).
* If you set this to a null value, the security mechanism used for connections shall be NULL, see zmq_null(3).
*
* @param username the username to set.
* @return true if the option was set, otherwise false.
*/
public boolean setPlainUsername(String username)
{
return base.setSocketOpt(zmq.ZMQ.ZMQ_PLAIN_USERNAME, username);
}
/**
* Sets the password for outgoing connections over TCP or IPC.
* If you set this to a non-null value, the security mechanism used for connections
* shall be PLAIN, see zmq_plain(7).
* If you set this to a null value, the security mechanism used for connections shall be NULL, see zmq_null(3).
*
* @param password the password to set.
* @return true if the option was set, otherwise false.
*/
public boolean setPlainPassword(String password)
{
return base.setSocketOpt(zmq.ZMQ.ZMQ_PLAIN_PASSWORD, password);
}
/**
* Sets the username for outgoing connections over TCP or IPC.
* If you set this to a non-null value, the security mechanism used for connections shall be PLAIN, see zmq_plain(7).
* If you set this to a null value, the security mechanism used for connections shall be NULL, see zmq_null(3).
*
* @param username the username to set.
* @return true if the option was set, otherwise false.
*/
public boolean setPlainUsername(byte[] username)
{
return base.setSocketOpt(zmq.ZMQ.ZMQ_PLAIN_USERNAME, username);
}
/**
* Sets the password for outgoing connections over TCP or IPC.
* If you set this to a non-null value, the security mechanism used for connections
* shall be PLAIN, see zmq_plain(7).
* If you set this to a null value, the security mechanism used for connections shall be NULL, see zmq_null(3).
*
* @param password the password to set.
* @return true if the option was set, otherwise false.
*/
public boolean setPlainPassword(byte[] password)
{
return base.setSocketOpt(zmq.ZMQ.ZMQ_PLAIN_PASSWORD, password);
}
/**
* The ZMQ_PLAIN_USERNAME option shall retrieve the last username
* set for the PLAIN security mechanism.
*
* @return the plain username.
*/
public String getPlainUsername()
{
return (String) base.getSocketOptx(zmq.ZMQ.ZMQ_PLAIN_USERNAME);
}
/**
* The ZMQ_PLAIN_PASSWORD option shall retrieve the last password
* set for the PLAIN security mechanism.
* The returned value MAY be empty.
*
* @return the plain password.
*/
public String getPlainPassword()
{
return (String) base.getSocketOptx(zmq.ZMQ.ZMQ_PLAIN_PASSWORD);
}
/**
* Defines whether the socket will act as server for CURVE security, see zmq_curve(7).
* A value of true means the socket will act as CURVE server.
* A value of false means the socket will not act as CURVE server,
* and its security role then depends on other option settings.
* Setting this to false shall reset the socket security to NULL.
* When you set this you must also set the server's secret key using the ZMQ_CURVE_SECRETKEY option.
* A server socket does not need to know its own public key.
*
* @param server true if the role of the socket should be server for CURVE mechanism
* @return true if the option was set
* @see #isAsServerCurve()
* @deprecated the naming is inconsistent with jzmq, please use {@link #setCurveServer(boolean)} instead
*/
@Deprecated
public boolean setAsServerCurve(boolean server)
{
return setCurveServer(server);
}
/**
* Defines whether the socket will act as server for CURVE security, see zmq_curve(7).
* A value of true means the socket will act as CURVE server.
* A value of false means the socket will not act as CURVE server,
* and its security role then depends on other option settings.
* Setting this to false shall reset the socket security to NULL.
* When you set this you must also set the server's secret key using the ZMQ_CURVE_SECRETKEY option.
* A server socket does not need to know its own public key.
*
* @param server true if the role of the socket should be server for CURVE mechanism
* @return true if the option was set
* @see #isAsServerCurve()
*/
public boolean setCurveServer(boolean server)
{
return setSocketOpt(zmq.ZMQ.ZMQ_CURVE_SERVER, server);
}
/**
* Tells if the socket will act as server for CURVE security.
*
* @return true if the role of the socket should be server for CURVE mechanism.
* @see #setAsServerCurve(boolean)
* @deprecated the naming is inconsistent with jzmq, please use {@link #getCurveServer()} instead
*/
@Deprecated
public boolean isAsServerCurve()
{
return getCurveServer();
}
/**
* Tells if the socket will act as server for CURVE security.
*
* @return true if the role of the socket should be server for CURVE mechanism.
* @see #setAsServerCurve(boolean)
*/
public boolean getCurveServer()
{
return (boolean) base.getSocketOptx(zmq.ZMQ.ZMQ_CURVE_SERVER);
}
/**
* Tells if the socket will act as server for CURVE security.
*
* @return true if the role of the socket should be server for CURVE mechanism.
* @see #setAsServerCurve(boolean)
* @deprecated the naming is inconsistent with jzmq, please use {@link #getCurveServer()} instead
*/
@Deprecated
public boolean getAsServerCurve()
{
return getCurveServer();
}
/**
* Sets the socket's long term public key.
* You must set this on CURVE client sockets, see zmq_curve(7).
* You can provide the key as 32 binary bytes, or as a 40-character string
* encoded in the Z85 encoding format.
* The public key must always be used with the matching secret key.
* To generate a public/secret key pair,
* use {@link zmq.io.mechanism.curve.Curve#keypair()} or {@link zmq.io.mechanism.curve.Curve#keypairZ85()}.
*
* @param key the curve public key
* @return true if the option was set, otherwise false
* @see #getCurvePublicKey()
*/
public boolean setCurvePublicKey(byte[] key)
{
return setSocketOpt(zmq.ZMQ.ZMQ_CURVE_PUBLICKEY, key);
}
/**
* Sets the socket's long term server key.
* You must set this on CURVE client sockets, see zmq_curve(7).
* You can provide the key as 32 binary bytes, or as a 40-character string
* encoded in the Z85 encoding format.
* This key must have been generated together with the server's secret key.
* To generate a public/secret key pair,
* use {@link zmq.io.mechanism.curve.Curve#keypair()} or {@link zmq.io.mechanism.curve.Curve#keypairZ85()}.
*
* @param key the curve server key
* @return true if the option was set, otherwise false
* @see #getCurveServerKey()
*/
public boolean setCurveServerKey(byte[] key)
{
return setSocketOpt(zmq.ZMQ.ZMQ_CURVE_SERVERKEY, key);
}
/**
* Sets the socket's long term secret key.
* You must set this on both CURVE client and server sockets, see zmq_curve(7).
* You can provide the key as 32 binary bytes, or as a 40-character string
* encoded in the Z85 encoding format.
* To generate a public/secret key pair,
* use {@link zmq.io.mechanism.curve.Curve#keypair()} or {@link zmq.io.mechanism.curve.Curve#keypairZ85()}.
*
* @param key the curve secret key
* @return true if the option was set, otherwise false
* @see #getCurveSecretKey()
*/
public boolean setCurveSecretKey(byte[] key)
{
return setSocketOpt(zmq.ZMQ.ZMQ_CURVE_SECRETKEY, key);
}
/**
* Retrieves the current long term public key for the socket in binary format of 32 bytes.
*
* @return key the curve public key
* @see #setCurvePublicKey(byte[])
*/
public byte[] getCurvePublicKey()
{
return (byte[]) base.getSocketOptx(zmq.ZMQ.ZMQ_CURVE_PUBLICKEY);
}
/**
* Retrieves the current server key for the socket in binary format of 32 bytes.
*
* @return key the curve server key
* @see #setCurveServerKey(byte[])
*/
public byte[] getCurveServerKey()
{
return (byte[]) base.getSocketOptx(zmq.ZMQ.ZMQ_CURVE_SERVERKEY);
}
/**
* Retrieves the current long term secret key for the socket in binary format of 32 bytes.
*
* @return key the curve secret key
* @see #setCurveSecretKey(byte[])
*/
public byte[] getCurveSecretKey()
{
return (byte[]) base.getSocketOptx(zmq.ZMQ.ZMQ_CURVE_SECRETKEY);
}
/**
* The ZMQ_MECHANISM option shall retrieve the current security mechanism for the socket.
*
* @return the current mechanism.
*/
public Mechanism getMechanism()
{
return Mechanism.find((Mechanisms) base.getSocketOptx(zmq.ZMQ.ZMQ_MECHANISM));
}
/**
* When set, the socket will automatically send a hello message when a new connection is made or accepted.
* You may set this on DEALER or ROUTER sockets.
* The combination with ZMQ_HEARTBEAT_IVL is powerful and simplify protocols,
* as now heartbeat and sending the hello message can be left out of protocols and be handled by zeromq.
*
* @param helloMsg
* @return true if the option was set, otherwise false
*/
public boolean setHelloMsg(byte[] helloMsg)
{
return setSocketOpt(zmq.ZMQ.ZMQ_HELLO_MSG, helloMsg);
}
/**
* Bind to network interface. Start listening for new connections.
*
* @param addr the endpoint to bind to.
* @return true if the socket was bound, otherwise false.
*/
public boolean bind(String addr)
{
boolean rc = base.bind(addr);
mayRaise();
return rc;
}
/**
* Bind to network interface to a random port. Start listening for new
* connections.
*
* @param addr the endpoint to bind to.
*/
public int bindToRandomPort(String addr)
{
return bindToRandomPort(addr, DYNFROM, DYNTO);
}
/**
* Bind to network interface to a random port. Start listening for new
* connections.
*
* @param addr the endpoint to bind to.
* @param min The minimum port in the range of ports to try.
* @param max The maximum port in the range of ports to try.
*/
public int bindToRandomPort(String addr, int min, int max)
{
int port;
for (int i = 0; i < 100; i++) { // hardcoded to 100 tries. should this be parametrised
port = zmq.util.Utils.randomInt(max - min + 1) + min;
if (base.bind(String.format("%s:%s", addr, port))) {
base.errno.set(0);
return port;
}
// port++;
}
throw new ZMQException("Could not bind socket to random port.", ZError.EADDRINUSE);
}
/**
* Connects the socket to an endpoint and then accepts incoming connections on that endpoint.
*
* The endpoint is a string consisting of a transport :// followed by an address.
*
* The transport specifies the underlying protocol to use.
*
* The address specifies the transport-specific address to connect to.
*
* ØMQ provides the the following transports:
*
* tcp - unicast transport using TCP
* ipc - local inter-process communication transport
* inproc - local in-process (inter-thread) communication transport
*
* Every ØMQ socket type except ZMQ_PAIR supports one-to-many and many-to-one semantics.
* The precise semantics depend on the socket type.
*
* For most transports and socket types the connection is not performed immediately but as needed by ØMQ.
*
* Thus a successful call to connect(String) does not mean that the connection was or could actually be established.
*
* Because of this, for most transports and socket types
* the order in which a server socket is bound and a client socket is connected to it does not matter.
*
* The first exception is when using the inproc:// transport: you must call {@link #bind(String)} before calling connect().
*
* The second exception are ZMQ_PAIR sockets, which do not automatically reconnect to endpoints.
*
* Following a connect(), for socket types except for ZMQ_ROUTER, the socket enters its normal ready state.
*
* By contrast, following a {@link #bind(String)} alone, the socket enters a mute state
* in which the socket blocks or drops messages according to the socket type.
*
* A ZMQ_ROUTER socket enters its normal ready state for a specific peer
* only when handshaking is complete for that peer, which may take an arbitrary time.
*
* @param addr the endpoint to connect to.
* @return true if the socket was connected, otherwise false.
*/
public boolean connect(String addr)
{
boolean rc = base.connect(addr);
mayRaise();
return rc;
}
/**
* Disconnect from remote application.
*
* @param addr the endpoint to disconnect from.
* @return true if successful.
*/
public boolean disconnect(String addr)
{
return base.termEndpoint(addr);
}
/**
* Stop accepting connections on a socket.
*
* @param addr the endpoint to unbind from.
* @return true if successful.
*/
public boolean unbind(String addr)
{
return base.termEndpoint(addr);
}
/**
* create outgoing connection from socket and return the connection routing id in thread-safe and atomic way.
* The function is supported only on the {@link SocketType#PEER} or {@link SocketType#RAW} socket types
* and would return `0` with 'errno' set to 'ENOTSUP' otherwise.
* @param addr the endpoint of the remote socket.
* @return the endpoint routing ID.
*/
public int connectPeer(String addr)
{
return base.connectPeer(addr);
}
/**
* Queues a message created from data, so it can be sent.
*
* @param msg the {@link Msg} to send. The message is either a single-part message by itself,
* or the last part of a multi-part message.
* @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message.
* This does not indicate that the message has been transmitted to the network.
*/
public boolean sendMsg(Msg msg)
{
return sendMsg(msg, 0);
}
/**
* Queues a multi-part message created from data, so it can be sent.
*
* @param msg the message to send. further message parts are to follow.
* @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message.
* This does not indicate that the message has been transmitted to the network.
*/
public boolean sendMsgMore(Msg msg)
{
return sendMsg(msg, zmq.ZMQ.ZMQ_SNDMORE);
}
/**
* Queues a message created from data, so it can be sent.
*
* @param msg the {@link Msg} to send. The message is either a single-part message by itself,
* or the last part of a multi-part message.
* @param flags a combination (with + or |) of the flags defined below:
*
* {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}:
* For socket types ({@link org.zeromq.ZMQ#DEALER DEALER}, {@link org.zeromq.ZMQ#PUSH PUSH})
* that block when there are no available peers (or all peers have full high-water mark),
* specifies that the operation should be performed in non-blocking mode.
* If the message cannot be queued on the socket, the method shall fail with errno set to EAGAIN.
* {@link org.zeromq.ZMQ#SNDMORE SNDMORE}:
* Specifies that the message being sent is a multi-part message,
* and that further message parts are to follow.
* 0 : blocking send of a single-part message or the last of a multi-part message
*
* @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message.
* This does not indicate that the message has been transmitted to the network.
*/
public boolean sendMsg(Msg msg, int flags)
{
if (base.send(msg, flags)) {
return true;
}
mayRaise();
return false;
}
/**
* Queues a message created from data, so it can be sent.
*
* @param data the data to send. The data is either a single-part message by itself,
* or the last part of a multi-part message.
* @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message.
* This does not indicate that the message has been transmitted to the network.
*/
public boolean send(String data)
{
return send(data.getBytes(CHARSET), 0);
}
/**
* Queues a multi-part message created from data, so it can be sent.
*
* @param data the data to send. further message parts are to follow.
* @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message.
* This does not indicate that the message has been transmitted to the network.
*/
public boolean sendMore(String data)
{
return send(data.getBytes(CHARSET), zmq.ZMQ.ZMQ_SNDMORE);
}
/**
* Queues a message created from data.
*
* @param data the data to send.
* @param flags a combination (with + or |) of the flags defined below:
*
* {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}:
* For socket types ({@link org.zeromq.ZMQ#DEALER DEALER}, {@link org.zeromq.ZMQ#PUSH PUSH})
* that block when there are no available peers (or all peers have full high-water mark),
* specifies that the operation should be performed in non-blocking mode.
* If the message cannot be queued on the socket, the method shall fail with errno set to EAGAIN.
* {@link org.zeromq.ZMQ#SNDMORE SNDMORE}:
* Specifies that the message being sent is a multi-part message,
* and that further message parts are to follow.
* 0 : blocking send of a single-part message or the last of a multi-part message
*
* @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message.
* This does not indicate that the message has been transmitted to the network.
*/
public boolean send(String data, int flags)
{
return send(data.getBytes(CHARSET), flags);
}
/**
* Queues a message created from data, so it can be sent.
*
* @param data the data to send. The data is either a single-part message by itself,
* or the last part of a multi-part message.
* @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message.
* This does not indicate that the message has been transmitted to the network.
*/
public boolean send(byte[] data)
{
return send(data, 0);
}
/**
* Queues a multi-part message created from data, so it can be sent.
*
* @param data the data to send. further message parts are to follow.
* @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message.
* This does not indicate that the message has been transmitted to the network.
*/
public boolean sendMore(byte[] data)
{
return send(data, zmq.ZMQ.ZMQ_SNDMORE);
}
/**
* Queues a message created from data, so it can be sent.
*
* @param data the data to send.
* @param flags a combination (with + or |) of the flags defined below:
*
* {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}:
* For socket types ({@link org.zeromq.ZMQ#DEALER DEALER}, {@link org.zeromq.ZMQ#PUSH PUSH})
* that block when there are no available peers (or all peers have full high-water mark),
* specifies that the operation should be performed in non-blocking mode.
* If the message cannot be queued on the socket, the method shall fail with errno set to EAGAIN.
* {@link org.zeromq.ZMQ#SNDMORE SNDMORE}:
* Specifies that the message being sent is a multi-part message,
* and that further message parts are to follow.
* 0 : blocking send of a single-part message or the last of a multi-part message
*
* @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message.
* This does not indicate that the message has been transmitted to the network.
*/
public boolean send(byte[] data, int flags)
{
zmq.Msg msg = new zmq.Msg(data);
if (base.send(msg, flags)) {
return true;
}
mayRaise();
return false;
}
/**
* Queues a message created from data, so it can be sent, the call be canceled by calling cancellationToken {@link CancellationToken#cancel()}.
* If the operation is canceled a ZMQException is thrown with error code set to {@link ZError#ECANCELED}.
* @param data the data to send.
* @param flags a combination (with + or |) of the flags defined below:
*
* {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}:
* For socket types ({@link org.zeromq.ZMQ#DEALER DEALER}, {@link org.zeromq.ZMQ#PUSH PUSH})
* that block when there are no available peers (or all peers have full high-water mark),
* specifies that the operation should be performed in non-blocking mode.
* If the message cannot be queued on the socket, the method shall fail with errno set to EAGAIN.
* {@link org.zeromq.ZMQ#SNDMORE SNDMORE}:
* Specifies that the message being sent is a multi-part message,
* and that further message parts are to follow.
* 0 : blocking send of a single-part message or the last of a multi-part message
*
* @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message.
* This does not indicate that the message has been transmitted to the network.
*/
public boolean send(byte[] data, int flags, CancellationToken cancellationToken)
{
zmq.Msg msg = new zmq.Msg(data);
if (base.send(msg, flags, cancellationToken.canceled)) {
return true;
}
mayRaise();
return false;
}
/**
* Queues a message created from data, so it can be sent.
*
* @param data the data to send.
* @param off the index of the first byte to be sent.
* @param length the number of bytes to be sent.
* @param flags a combination (with + or |) of the flags defined below:
*
* {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}:
* For socket types ({@link org.zeromq.ZMQ#DEALER DEALER}, {@link org.zeromq.ZMQ#PUSH PUSH})
* that block when there are no available peers (or all peers have full high-water mark),
* specifies that the operation should be performed in non-blocking mode.
* If the message cannot be queued on the socket, the method shall fail with errno set to EAGAIN.
* {@link org.zeromq.ZMQ#SNDMORE SNDMORE}:
* Specifies that the message being sent is a multi-part message,
* and that further message parts are to follow.
* 0 : blocking send of a single-part message or the last of a multi-part message
*
* @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message.
* This does not indicate that the message has been transmitted to the network.
*/
public boolean send(byte[] data, int off, int length, int flags)
{
byte[] copy = new byte[length];
System.arraycopy(data, off, copy, 0, length);
zmq.Msg msg = new zmq.Msg(copy);
if (base.send(msg, flags)) {
return true;
}
mayRaise();
return false;
}
/**
* Queues a message created from data, so it can be sent.
*
* @param data ByteBuffer payload
* @param flags a combination (with + or |) of the flags defined below:
*
* {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}:
* For socket types ({@link org.zeromq.ZMQ#DEALER DEALER}, {@link org.zeromq.ZMQ#PUSH PUSH})
* that block when there are no available peers (or all peers have full high-water mark),
* specifies that the operation should be performed in non-blocking mode.
* If the message cannot be queued on the socket, the method shall fail with errno set to EAGAIN.
* {@link org.zeromq.ZMQ#SNDMORE SNDMORE}:
* Specifies that the message being sent is a multi-part message,
* and that further message parts are to follow.
* 0 : blocking send of a single-part message or the last of a multi-part message
*
* @return the number of bytes queued, -1 on error
*/
public int sendByteBuffer(ByteBuffer data, int flags)
{
zmq.Msg msg = new zmq.Msg(data);
if (base.send(msg, flags)) {
return msg.size();
}
mayRaise();
return -1;
}
/**
* Queues a 'picture' message to the socket (or actor), so it can be sent.
*
* @param picture The picture is a string that defines the type of each frame.
* This makes it easy to send a complex multiframe message in
* one call. The picture can contain any of these characters,
* each corresponding to zero or one arguments:
*
* Type of arguments
* i = int (stores signed integer)
* 1 = byte (stores 8-bit unsigned integer)
* 2 = int (stores 16-bit unsigned integer)
* 4 = long (stores 32-bit unsigned integer)
* 8 = long (stores 64-bit unsigned integer)
* s = String
* b = byte[]
* f = ZFrame
* m = ZMsg (sends all frames in the ZMsg)
* z = sends zero-sized frame (0 arguments)
*
* Note that s, b, f and m are encoded the same way and the choice is
* offered as a convenience to the sender, which may or may not already
* have data in a ZFrame or ZMsg. Does not change or take ownership of
* any arguments.
*
* Also see {@link #recvPicture(String)}} how to recv a
* multiframe picture.
* @param args Arguments according to the picture
* @return true if successful, false if sending failed for any reason
*/
@Draft
public boolean sendPicture(String picture, Object... args)
{
return new ZPicture().sendPicture(this, picture, args);
}
/**
* Queues a binary encoded 'picture' message to the socket (or actor), so it can be sent.
* This method is similar to {@link #sendPicture(String, Object...)}, except the arguments
* are encoded in a binary format that is compatible with zproto, and is designed to reduce
* memory allocations.
*
* @param picture The picture argument is a string that defines the
* type of each argument. Supports these argument types:
*
*
* Type of arguments
* pattern java type zproto type
* 1 int type = "number" size = "1"
* 2 int type = "number" size = "2"
* 4 long type = "number" size = "3"
* 8 long type = "number" size = "4"
* s String, 0-255 chars type = "string"
* S String, 0-2^32-1 chars type = "longstr"
* c byte[], 0-2^32-1 bytes type = "chunk"
* f ZFrame type = "frame"
* m ZMsg type = "msg"
*
* @param args Arguments according to the picture
* @return true when it has been queued on the socket and ØMQ has assumed responsibility for the message.
* This does not indicate that the message has been transmitted to the network.
* @api.note Does not change or take ownership of any arguments.
*/
@Draft
public boolean sendBinaryPicture(String picture, Object... args)
{
return new ZPicture().sendBinaryPicture(this, picture, args);
}
/**
* Receives a message.
*
* @return the message received; null on error.
*/
public Msg recvMsg()
{
return recvMsg(0);
}
/**
* Receives a message.
*
* @param flags either:
*
* {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}:
* Specifies that the operation should be performed in non-blocking mode.
* If there are no messages available on the specified socket,
* the method shall fail with errno set to EAGAIN and return null.
* 0 : receive operation blocks until one message is successfully retrieved,
* or stops when timeout set by {@link #setReceiveTimeOut(int)} expires.
*
* @return the message received; null on error.
*/
public Msg recvMsg(int flags)
{
zmq.Msg msg = base.recv(flags);
if (msg != null) {
return msg;
}
mayRaise();
return null;
}
/**
* Receives a message.
*
* @return the message received, as an array of bytes; null on error.
*/
public byte[] recv()
{
return recv(0);
}
/**
* Receives a message.
*
* If possible, a reference to the data is returned, without copy.
* Otherwise a new byte array will be allocated and the data will be copied.
*
* @param flags either:
*
* {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}:
* Specifies that the operation should be performed in non-blocking mode.
* If there are no messages available on the specified socket,
* the method shall fail with errno set to EAGAIN and return null.
* 0 : receive operation blocks until one message is successfully retrieved,
* or stops when timeout set by {@link #setReceiveTimeOut(int)} expires.
*
* @return the message received, as an array of bytes; null on error.
*/
public byte[] recv(int flags)
{
zmq.Msg msg = base.recv(flags);
if (msg != null) {
return msg.data();
}
mayRaise();
return null;
}
/**
* Receives a message, the call be canceled by calling cancellationToken {@link CancellationToken#cancel()}.
* If the operation is canceled a ZMQException is thrown with error code set to {@link ZError#ECANCELED}.
*
* If possible, a reference to the data is returned, without copy.
* Otherwise a new byte array will be allocated and the data will be copied.
*
* @param flags either:
*
* {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}:
* Specifies that the operation should be performed in non-blocking mode.
* If there are no messages available on the specified socket,
* the method shall fail with errno set to EAGAIN and return null.
* 0 : receive operation blocks until one message is successfully retrieved,
* or stops when timeout set by {@link #setReceiveTimeOut(int)} expires.
*
* @param cancellationToken token to control cancellation of the receive operation.
* The token can be created by calling {@link #createCancellationToken() }.
* @return the message received, as an array of bytes; null on error.
*/
public byte[] recv(int flags, CancellationToken cancellationToken)
{
zmq.Msg msg = base.recv(flags, cancellationToken.canceled);
if (msg != null) {
return msg.data();
}
mayRaise();
return null;
}
/**
* Receives a message in to a specified buffer.
*
* @param buffer byte[] to copy zmq message payload in to.
* @param offset offset in buffer to write data
* @param len max bytes to write to buffer.
* If len is smaller than the incoming message size,
* the message will be truncated.
* @param flags either:
*
* {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}:
* Specifies that the operation should be performed in non-blocking mode.
* If there are no messages available on the specified socket,
* the method shall fail with errno set to EAGAIN and return null.
* 0 : receive operation blocks until one message is successfully retrieved,
* or stops when timeout set by {@link #setReceiveTimeOut(int)} expires.
*
* @return the number of bytes read, -1 on error
*/
public int recv(byte[] buffer, int offset, int len, int flags)
{
zmq.Msg msg = base.recv(flags);
if (msg != null) {
return msg.getBytes(0, buffer, offset, len);
}
return -1;
}
/**
* Receives a message into the specified ByteBuffer.
*
* @param buffer the buffer to copy the zmq message payload into
* @param flags either:
*
* {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}:
* Specifies that the operation should be performed in non-blocking mode.
* If there are no messages available on the specified socket,
* the method shall fail with errno set to EAGAIN and return null.
* 0 : receive operation blocks until one message is successfully retrieved,
* or stops when timeout set by {@link #setReceiveTimeOut(int)} expires.
*
* @return the number of bytes read, -1 on error
*/
public int recvByteBuffer(ByteBuffer buffer, int flags)
{
zmq.Msg msg = base.recv(flags);
if (msg != null) {
buffer.put(msg.buf());
return msg.size();
}
mayRaise();
return -1;
}
/**
* @return the message received, as a String object; null on no message.
*/
public String recvStr()
{
return recvStr(0);
}
/**
* Receives a message as a string.
*
* @param flags either:
*
* {@link org.zeromq.ZMQ#DONTWAIT DONTWAIT}:
* Specifies that the operation should be performed in non-blocking mode.
* If there are no messages available on the specified socket,
* the method shall fail with errno set to EAGAIN and return null.
* 0 : receive operation blocks until one message is successfully retrieved,
* or stops when timeout set by {@link #setReceiveTimeOut(int)} expires.
*
* @return the message received, as a String object; null on no message.
*/
public String recvStr(int flags)
{
byte[] msg = recv(flags);
if (msg != null) {
return new String(msg, CHARSET);
}
return null;
}
/**
* Receive a 'picture' message to the socket (or actor).
*
*
* @param picture The picture is a string that defines the type of each frame.
* This makes it easy to recv a complex multiframe message in
* one call. The picture can contain any of these characters,
* each corresponding to zero or one elements in the result:
*
*
*
* Type of arguments
* i = int (stores signed integer)
* 1 = int (stores 8-bit unsigned integer)
* 2 = int (stores 16-bit unsigned integer)
* 4 = long (stores 32-bit unsigned integer)
* 8 = long (stores 64-bit unsigned integer)
* s = String
* b = byte[]
* f = ZFrame (creates zframe)
* m = ZMsg (creates a zmsg with the remaing frames)
* z = null, asserts empty frame (0 arguments)
*
*
* Also see {@link #sendPicture(String, Object...)} how to send a
* multiframe picture.
*
* @return the picture elements as object array
*/
@Draft
public Object[] recvPicture(String picture)
{
return new ZPicture().recvPicture(this, picture);
}
/**
* Receive a binary encoded 'picture' message from the socket (or actor).
* This method is similar to {@link #recv()}, except the arguments are encoded
* in a binary format that is compatible with zproto, and is designed to
* reduce memory allocations.
*
* @param picture The picture argument is a string that defines
* the type of each argument. See {@link #sendBinaryPicture(String, Object...)}
* for the supported argument types.
* @return the picture elements as object array
**/
@Draft
public Object[] recvBinaryPicture(final String picture)
{
return new ZPicture().recvBinaryPicture(this, picture);
}
/**
* Start a monitoring socket where events can be received.
*
* Lets an application thread track socket events (like connects) on a ZeroMQ socket.
* Each call to this method creates a {@link ZMQ#PAIR} socket and binds that to the specified inproc:// endpoint.
* To collect the socket events, you must create your own PAIR socket, and connect that to the endpoint.
*
* Supports only connection-oriented transports, that is, TCP, IPC.
*
* @param addr the endpoint to receive events from. (must be inproc transport)
* @param events the events of interest. A bitmask of the socket events you wish to monitor. To monitor all events, use the event value {@link ZMQ#EVENT_ALL}.
* @return true if monitor socket setup is successful
* @throws ZMQException
*/
public boolean monitor(String addr, int events)
{
return base.monitor(addr, events);
}
/**
* Register a custom event consumer.
*
* @param consumer The event consumer.
* @param events the events of interest. A bitmask of the socket events you wish to monitor. To monitor all events, use the event value {@link ZMQ#EVENT_ALL}.
* @return true if consumer setup is successful
* @throws ZMQException
*/
public boolean setEventHook(ZEvent.ZEventConsummer consumer, int events)
{
return base.setEventHook(consumer, events);
}
protected void mayRaise()
{
int errno = base.errno();
if (errno != 0 && errno != zmq.ZError.EAGAIN) {
throw new ZMQException(errno);
}
}
public int errno()
{
return base.errno();
}
@Override
public String toString()
{
return base.toString();
}
public enum Mechanism
{
NULL(Mechanisms.NULL),
PLAIN(Mechanisms.PLAIN),
CURVE(Mechanisms.CURVE);
// TODO add GSSAPI once it is implemented
private final Mechanisms mech;
Mechanism(Mechanisms zmq)
{
this.mech = zmq;
}
private static Mechanism find(Mechanisms mech)
{
for (Mechanism candidate : values()) {
if (candidate.mech == mech) {
return candidate;
}
}
return null;
}
}
/**
* Create a {@link CancellationToken} to cancel send/receive operations for this socket.
* @return a new cancellation token associated with this socket.
*/
public CancellationToken createCancellationToken()
{
return new CancellationToken(base);
}
}
/**
* Provides a mechanism for applications to multiplex input/output events in a level-triggered fashion over a set of sockets
*/
public static class Poller implements Closeable
{
/**
* For ØMQ sockets, at least one message may be received from the socket without blocking.
*
* For standard sockets this is equivalent to the POLLIN flag of the poll() system call
* and generally means that at least one byte of data may be read from fd without blocking.
*/
public static final int POLLIN = zmq.ZMQ.ZMQ_POLLIN;
/**
* For ØMQ sockets, at least one message may be sent to the socket without blocking.
*
* For standard sockets this is equivalent to the POLLOUT flag of the poll() system call
* and generally means that at least one byte of data may be written to fd without blocking.
*/
public static final int POLLOUT = zmq.ZMQ.ZMQ_POLLOUT;
/**
* For standard sockets, this flag is passed through {@link zmq.ZMQ#poll(Selector, zmq.poll.PollItem[], long)} to the underlying poll() system call
* and generally means that some sort of error condition is present on the socket specified by fd.
*
* For ØMQ sockets this flag has no effect if set in events, and shall never be returned in revents by {@link zmq.ZMQ#poll(Selector, zmq.poll.PollItem[], long)}.
*/
public static final int POLLERR = zmq.ZMQ.ZMQ_POLLERR;
private static final int SIZE_DEFAULT = 32;
private static final int SIZE_INCREMENT = 16;
private final Selector selector;
private final Context context;
private final List items;
private long timeout;
/**
* Class constructor.
*
* @param context a 0MQ context previously created.
* @param size the number of Sockets this poller will contain.
*/
protected Poller(Context context, int size)
{
assert (context != null);
this.context = context;
selector = context.selector();
assert (selector != null);
items = new ArrayList<>(size);
timeout = -1L;
}
/**
* Class constructor.
*
* @param context a 0MQ context previously created.
*/
protected Poller(Context context)
{
this(context, SIZE_DEFAULT);
}
@Override
public void close()
{
context.close(selector);
}
/**
* Register a Socket for polling on all events.
*
* @param socket the Socket we are registering.
* @return the index identifying this Socket in the poll set.
*/
public int register(Socket socket)
{
return register(socket, POLLIN | POLLOUT | POLLERR);
}
/**
* Register a Channel for polling on all events.
*
* @param channel the Channel we are registering.
* @return the index identifying this Channel in the poll set.
*/
public int register(SelectableChannel channel)
{
return register(channel, POLLIN | POLLOUT | POLLERR);
}
/**
* Register a Socket for polling on the specified events.
*
* Automatically grow the internal representation if needed.
*
* @param socket the Socket we are registering.
* @param events a mask composed by XORing POLLIN, POLLOUT and POLLERR.
* @return the index identifying this Socket in the poll set.
*/
public int register(Socket socket, int events)
{
return registerInternal(new PollItem(socket, events));
}
/**
* Register a Socket for polling on the specified events.
*
* Automatically grow the internal representation if needed.
*
* @param channel the Channel we are registering.
* @param events a mask composed by XORing POLLIN, POLLOUT and POLLERR.
* @return the index identifying this Channel in the poll set.
*/
public int register(SelectableChannel channel, int events)
{
return registerInternal(new PollItem(channel, events));
}
/**
* Register a Channel for polling on the specified events.
*
* Automatically grow the internal representation if needed.
*
* @param item the PollItem we are registering.
* @return the index identifying this Channel in the poll set.
*/
public int register(PollItem item)
{
return registerInternal(item);
}
/**
* Register a Socket for polling on the specified events.
*
* Automatically grow the internal representation if needed.
*
* @param item the PollItem we are registering.
* @return the index identifying this Socket in the poll set.
*/
private int registerInternal(PollItem item)
{
items.add(item);
return items.size() - 1;
}
/**
* Unregister a Socket for polling on the specified events.
*
* @param socket the Socket to be unregistered
*/
public void unregister(Socket socket)
{
unregisterInternal(socket);
}
/**
* Unregister a Socket for polling on the specified events.
*
* @param channel the Socket to be unregistered
*/
public void unregister(SelectableChannel channel)
{
unregisterInternal(channel);
}
/**
* Unregister a Socket for polling on the specified events.
*
* @param socket the Socket to be unregistered
*/
private void unregisterInternal(Object socket)
{
items.removeIf(item -> item.socket == socket || item.getRawSocket() == socket);
}
/**
* Get the PollItem associated with an index.
*
* @param index the desired index.
* @return the PollItem associated with that index (or null).
*/
public PollItem getItem(int index)
{
if (index < 0 || index >= items.size()) {
return null;
}
return this.items.get(index);
}
/**
* Get the socket associated with an index.
*
* @param index the desired index.
* @return the Socket associated with that index (or null).
*/
public Socket getSocket(int index)
{
if (index < 0 || index >= items.size()) {
return null;
}
return items.get(index).socket;
}
/**
* Get the current poll timeout.
*
* @return the current poll timeout in milliseconds.
* @deprecated Timeout handling has been moved to the poll() methods.
*/
@Deprecated
public long getTimeout()
{
return this.timeout;
}
/**
* Set the poll timeout.
*
* @param timeout the desired poll timeout in milliseconds.
* @deprecated Timeout handling has been moved to the poll() methods.
*/
@Deprecated
public void setTimeout(long timeout)
{
if (timeout >= -1L) {
this.timeout = timeout;
}
}
/**
* Get the current poll set size.
*
* @return the current poll set size.
*/
public int getSize()
{
return items.size();
}
/**
* Get the index for the next position in the poll set size.
*
* @deprecated use getSize instead
* @return the index for the next position in the poll set size.
*/
@Deprecated
public int getNext()
{
return items.size();
}
/**
* Issue a poll call. If the poller's internal timeout value
* has been set, use that value as timeout; otherwise, block
* indefinitely.
*
* @return how many objects where signaled by poll ().
*/
public int poll()
{
long tout = -1L;
if (this.timeout > -1L) {
tout = this.timeout;
}
return poll(tout);
}
/**
* Issue a poll call, using the specified timeout value.
*
* Since ZeroMQ 3.0, the timeout parameter is in milliseconds ,
* but prior to this the unit was microseconds .
*
* @param tout the timeout, as per zmq_poll ();
* if -1, it will block indefinitely until an event
* happens; if 0, it will return immediately;
* otherwise, it will wait for at most that many
* milliseconds/microseconds (see above).
* @return how many objects where signaled by poll ()
* @see "http://api.zeromq.org/3-0:zmq-poll"
*/
public int poll(long tout)
{
if (tout < -1) {
return 0;
}
if (items.isEmpty()) {
return 0;
}
zmq.poll.PollItem[] pollItems = new zmq.poll.PollItem[items.size()];
for (int i = 0, j = 0; i < items.size(); i++) {
if (items.get(i) != null) {
pollItems[j++] = items.get(i).base;
}
}
try {
return zmq.ZMQ.poll(selector, pollItems, items.size(), tout);
}
catch (ZError.IOException e) {
if (context.isTerminated()) {
return 0;
}
else {
throw (e);
}
}
}
/**
* Check whether the specified element in the poll set was signaled for input.
*
* @param index of element
* @return true if the element was signaled.
*/
public boolean pollin(int index)
{
if (index < 0 || index >= items.size()) {
return false;
}
return items.get(index).isReadable();
}
/**
* Check whether the specified element in the poll set was signaled for output.
*
* @param index of element
* @return true if the element was signaled.
*/
public boolean pollout(int index)
{
if (index < 0 || index >= items.size()) {
return false;
}
return items.get(index).isWritable();
}
/**
* Check whether the specified element in the poll set was signaled for error.
*
* @param index of element
* @return true if the element was signaled.
*/
public boolean pollerr(int index)
{
if (index < 0 || index >= items.size()) {
return false;
}
return items.get(index).isError();
}
}
public static class PollItem
{
private final zmq.poll.PollItem base;
private final Socket socket;
public PollItem(Socket socket, int ops)
{
this.socket = socket;
base = new zmq.poll.PollItem(socket.base, ops);
}
public PollItem(SelectableChannel channel, int ops)
{
base = new zmq.poll.PollItem(channel, ops);
socket = null;
}
final zmq.poll.PollItem base()
{
return base;
}
public final SelectableChannel getRawSocket()
{
return base.getRawSocket();
}
public final Socket getSocket()
{
return socket;
}
public final boolean isReadable()
{
return base.isReadable();
}
public final boolean isWritable()
{
return base.isWritable();
}
public final boolean isError()
{
return base.isError();
}
public final int readyOps()
{
return base.readyOps();
}
@Override
public int hashCode()
{
return base.hashCode();
}
@Override
public boolean equals(Object obj)
{
if (!(obj instanceof PollItem)) {
return false;
}
PollItem target = (PollItem) obj;
if (socket != null && socket == target.socket) {
return true;
}
return getRawSocket() != null && getRawSocket() == target.getRawSocket();
}
}
/**
* Inner class: Event.
* Monitor socket event class
* @deprecated Uses {@link org.zeromq.ZEvent} instead
*/
@Deprecated
public static class Event
{
private final int event;
// To keep backward compatibility, the old value field only store integer
// The resolved value (Error, channel or other) is stored in resolvedValue field.
private final Object value;
private final Object resolvedValue;
private final String address;
public Event(int event, Object value, String address)
{
this.event = event;
this.value = value;
this.address = address;
this.resolvedValue = value;
}
private Event(int event, Object value, Object resolvedValue, String address)
{
this.event = event;
this.value = value;
this.address = address;
this.resolvedValue = resolvedValue;
}
/**
* Receive an event from a monitor socket.
*
* @param socket the socket
* @param flags the flags to apply to the receive operation.
* @return the received event or null if no message was received.
* @throws ZMQException
*/
public static Event recv(Socket socket, int flags)
{
zmq.ZMQ.Event e = zmq.ZMQ.Event.read(socket.base, flags);
if (e == null) {
return null;
}
Object resolvedValue;
switch (e.event) {
case zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL:
resolvedValue = ZMonitor.ProtocolCode.findByCode((Integer) e.arg);
break;
case zmq.ZMQ.ZMQ_EVENT_CLOSE_FAILED:
case zmq.ZMQ.ZMQ_EVENT_ACCEPT_FAILED:
case zmq.ZMQ.ZMQ_EVENT_BIND_FAILED:
case zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL:
resolvedValue = Error.findByCode((Integer) e.arg);
break;
case zmq.ZMQ.ZMQ_EVENT_CONNECTED:
case zmq.ZMQ.ZMQ_EVENT_LISTENING:
case zmq.ZMQ.ZMQ_EVENT_ACCEPTED:
case zmq.ZMQ.ZMQ_EVENT_CLOSED:
case zmq.ZMQ.ZMQ_EVENT_DISCONNECTED:
resolvedValue = e.getChannel(socket.base);
break;
case zmq.ZMQ.ZMQ_EVENT_CONNECT_DELAYED:
case zmq.ZMQ.ZMQ_EVENT_HANDSHAKE_SUCCEEDED:
case zmq.ZMQ.ZMQ_EVENT_MONITOR_STOPPED:
resolvedValue = null;
break;
default:
resolvedValue = e.arg;
}
return new Event(e.event, e.arg, resolvedValue, e.addr);
}
/**
* Receive an event from a monitor socket.
* Does a blocking recv.
*
* @param socket the socket
* @return the received event.
* @throws ZMQException
*/
public static Event recv(Socket socket)
{
return Event.recv(socket, 0);
}
public int getEvent()
{
return event;
}
public Object getValue()
{
return value;
}
public String getAddress()
{
return address;
}
/**
* Used to check if the event is an error.
*
* Generally, any event that define the errno is
* considered as an error.
* @return true if the evant was an error
*/
public boolean isError()
{
switch (event) {
case EVENT_CLOSE_FAILED:
case EVENT_ACCEPT_FAILED:
case EVENT_BIND_FAILED:
case HANDSHAKE_FAILED_PROTOCOL:
case HANDSHAKE_FAILED_NO_DETAIL:
return true;
default:
return false;
}
}
/**
* Used to check if the event is a warning.
*
* Generally, any event that return an authentication failure is
* considered as a warning.
* @return
*/
public boolean isWarn()
{
switch (event) {
case HANDSHAKE_FAILED_AUTH:
return true;
default:
return false;
}
}
/**
* Return the argument as an integer or a Enum of the appropriate type if available.
*
* It returns objects of type:
*
* {@link org.zeromq.ZMonitor.ProtocolCode} for a handshake protocol error.
* {@link org.zeromq.ZMQ.Error} for any other error.
* {@link java.lang.Integer} when available.
* null when no relevant value available.
*
* @param The expected type of the returned object
* @return The resolved value.
*/
@SuppressWarnings("unchecked")
public M resolveValue()
{
return (M) resolvedValue;
}
}
/**
* Class that interfaces the generation of CURVE key pairs.
*
* The CURVE mechanism defines a mechanism for secure authentication and confidentiality for communications between a client and a server.
* CURVE is intended for use on public networks.
* The CURVE mechanism is defined by this document: http://rfc.zeromq.org/spec:25.
*
* Client and server roles
*
* A socket using CURVE can be either client or server, at any moment, but not both. The role is independent of bind/connect direction.
* A socket can change roles at any point by setting new options. The role affects all connect and bind calls that follow it.
*
* To become a CURVE server, the application sets the {@link ZMQ.Socket#setAsServerCurve(boolean)} option on the socket,
* and then sets the {@link ZMQ.Socket#setCurveSecretKey(byte[])} option to provide the socket with its long-term secret key.
* The application does not provide the socket with its long-term public key, which is used only by clients.
*
* To become a CURVE client, the application sets the {@link ZMQ.Socket#setCurveServerKey(byte[])} option
* with the long-term public key of the server it intends to connect to, or accept connections from, next.
* The application then sets the {@link ZMQ.Socket#setCurvePublicKey(byte[])} and {@link ZMQ.Socket#setCurveSecretKey(byte[])} options with its client long-term key pair.
* If the server does authentication it will be based on the client's long term public key.
*
* Key encoding
*
* The standard representation for keys in source code is either 32 bytes of base 256 (binary) data,
* or 40 characters of base 85 data encoded using the Z85 algorithm defined by http://rfc.zeromq.org/spec:32.
* The Z85 algorithm is designed to produce printable key strings for use in configuration files, the command line, and code.
* There is a reference implementation in C at https://github.com/zeromq/rfc/tree/master/src.
*
* Test key values
*
* For test cases, the client shall use this long-term key pair (specified as hexadecimal and in Z85):
*
* public:
* BB88471D65E2659B30C55A5321CEBB5AAB2B70A398645C26DCA2B2FCB43FC518
* {@code Yne@$w-vo
*
* secret:
* 7BB864B489AFA3671FBE69101F94B38972F24816DFB01B51656B3FEC8DFD0888
* {@code D:)Q[IlAW!ahhC2ac:9*A}h:p?([4%wOTJ%JR%cs}
*
*
*
* And the server shall use this long-term key pair (specified as hexadecimal and in Z85):
*
* public:
* 54FCBA24E93249969316FB617C872BB0C1D1FF14800427C594CBFACF1BC2D652
* {@code rq:rM>}U?@Lns47E1%kR.o@n%FcmmsL/@{H8]yf7}
*
* secret:
* 8E0BDD697628B91D8F245587EE95C5B04D48963F79259877B49CD9063AEAD3B7
* {@code JTKVSB%%)wK0E.X)V>+}o?pNmC{O&4W4b!Ni{Lh6}
*
*
*/
public static class Curve
{
public static final int KEY_SIZE = Options.CURVE_KEYSIZE;
public static final int KEY_SIZE_Z85 = Options.CURVE_KEYSIZE_Z85;
/**
* Returns a newly generated random keypair consisting of a public key
* and a secret key.
*
* The keys are encoded using {@link #z85Encode}.
*
* @return Randomly generated {@link KeyPair}
*/
public static KeyPair generateKeyPair()
{
String[] keys = new zmq.io.mechanism.curve.Curve().keypairZ85();
return new KeyPair(keys[0], keys[1]);
}
/**
* The function shall decode given key encoded as Z85 string into byte array.
* The length of string shall be divisible by 5.
* The decoding shall follow the ZMQ RFC 32 specification.
*
* @param key Key to be decoded
* @return The resulting key as byte array
*/
public static byte[] z85Decode(String key)
{
return Z85.decode(key);
}
/**
* Encodes the binary block specified by data into a string.
* The size of the binary block must be divisible by 4.
* A 32-byte CURVE key is encoded as 40 ASCII characters plus a null terminator.
* The function shall encode the binary block specified into a string.
* The encoding shall follow the ZMQ RFC 32 specification.
*
* @param key Key to be encoded
* @return The resulting key as String in Z85
*/
public static String z85Encode(byte[] key)
{
return zmq.io.mechanism.curve.Curve.z85EncodePublic(key);
}
/**
* A container for a public and a corresponding secret key.
* Keys have to be encoded in Z85 format.
*/
public static class KeyPair
{
/**
* Z85-encoded public key.
*/
public final String publicKey;
/**
* Z85-encoded secret key.
*/
public final String secretKey;
public KeyPair(final String publicKey, final String secretKey)
{
Utils.checkArgument(publicKey != null, "Public key cannot be null");
Utils.checkArgument(publicKey.length() == Curve.KEY_SIZE_Z85, "Public key has to be Z85 format");
Utils.checkArgument(secretKey == null || secretKey.length() == Curve.KEY_SIZE_Z85, "Secret key has to be null or in Z85 format");
this.publicKey = publicKey;
this.secretKey = secretKey;
}
}
}
/**
* A cancellation token that allows canceling ongoing Socket send/receive operations.
* When calling send/receive you can provide the cancellation token as an additional parameter.
* To create a cancellation token call {@link Socket#createCancellationToken()}.
*
*/
public static class CancellationToken
{
protected final AtomicBoolean canceled;
final SocketBase socket;
protected CancellationToken(SocketBase socket)
{
this.socket = socket;
canceled = new AtomicBoolean(false);
}
public boolean isCancellationRequested()
{
return canceled.get();
}
/**
* Reset the cancellation token in order to reuse the token with another send/receive call.
*/
public void reset()
{
canceled.set(false);
}
/**
* Cancel a pending the send/receive operation.
*/
public void cancel()
{
socket.cancel(canceled);
}
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZMQException.java 0000664 0000000 0000000 00000001335 14557711263 0022760 0 ustar 00root root 0000000 0000000 package org.zeromq;
import zmq.ZError;
public class ZMQException extends UncheckedZMQException
{
private static final long serialVersionUID = -978820750094924644L;
private final int code;
public ZMQException(int errno)
{
super("Errno " + errno);
code = errno;
}
public ZMQException(String message, int errno)
{
super(message);
code = errno;
}
public ZMQException(String message, int errno, Throwable cause)
{
super(message, cause);
code = errno;
}
public int getErrorCode()
{
return code;
}
@Override
public String toString()
{
return super.toString() + " : " + ZError.toString(code);
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZMQQueue.java 0000664 0000000 0000000 00000001275 14557711263 0022111 0 ustar 00root root 0000000 0000000 package org.zeromq;
import org.zeromq.ZMQ.Context;
import org.zeromq.ZMQ.Socket;
public class ZMQQueue implements Runnable
{
private final Socket inSocket;
private final Socket outSocket;
/**
* Class constructor.
*
* @param context
* a 0MQ context previously created.
* @param inSocket
* input socket
* @param outSocket
* output socket
*/
public ZMQQueue(Context context, Socket inSocket, Socket outSocket)
{
this.inSocket = inSocket;
this.outSocket = outSocket;
}
@Override
public void run()
{
zmq.ZMQ.proxy(inSocket.base(), outSocket.base(), null);
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZMonitor.java 0000664 0000000 0000000 00000044571 14557711263 0022224 0 ustar 00root root 0000000 0000000 package org.zeromq;
import java.io.Closeable;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.zeromq.ZMQ.Socket;
import org.zeromq.proto.ZNeedle;
import zmq.util.Objects;
/**
* The ZMonitor actor provides an API for obtaining socket events such as
* connected, listen, disconnected, etc. Socket events are only available
* for sockets connecting or bound to ipc:// and tcp:// endpoints.
*/
public class ZMonitor implements Closeable
{
/**
* High-level representation of an event.
*/
public static final class ZEvent
{
// Enum.values return a different array on each call, so keep it to avoid useless allocations
private static final Event[] EVENTVALUES = Event.values();
/**
* The type of the event. (never null)
*/
public final Event type;
/**
* The numerical code of the event. (useful in case of unrecognized event, in which case type is ALL)
*/
public final int code;
/**
* The address of the source of the event.
*/
public final String address;
/**
* String representation of the event value.
* Nature of the value depends on the type of event. May be null
*/
public final String value;
private ZEvent(ZMsg msg)
{
assert (msg != null);
assert (msg.size() == 1 || msg.size() == 2) : msg.size();
final ZFrame frame = msg.pop();
final ZNeedle needle = new ZNeedle(frame);
type = EVENTVALUES[needle.getNumber1()];
code = needle.getNumber4();
address = needle.getString();
if (msg.isEmpty()) {
value = null;
}
else {
value = msg.popString();
}
}
@SuppressWarnings("deprecation")
public ZEvent(ZMQ.Event event)
{
code = event.getEvent();
address = event.getAddress();
type = Event.findByCode(code);
Object tryValue = event.resolveValue();
if (tryValue != null) {
value = tryValue.toString();
}
else {
value = null;
}
}
@Override
public String toString()
{
return "ZEvent [type=" + type + ", code=" + code + ", address=" + address + ", value=" + value + "]";
}
}
/**
* Enumerates types of monitoring events.
*/
public enum Event
{
CONNECTED(ZMQ.EVENT_CONNECTED),
CONNECT_DELAYED(ZMQ.EVENT_CONNECT_DELAYED),
CONNECT_RETRIED(ZMQ.EVENT_CONNECT_RETRIED),
LISTENING(ZMQ.EVENT_LISTENING),
BIND_FAILED(ZMQ.EVENT_BIND_FAILED),
ACCEPTED(ZMQ.EVENT_ACCEPTED),
ACCEPT_FAILED(ZMQ.EVENT_ACCEPT_FAILED),
CLOSED(ZMQ.EVENT_CLOSED),
CLOSE_FAILED(ZMQ.EVENT_CLOSE_FAILED),
DISCONNECTED(ZMQ.EVENT_DISCONNECTED),
MONITOR_STOPPED(ZMQ.EVENT_MONITOR_STOPPED),
HANDSHAKE_FAILED_NO_DETAIL(ZMQ.HANDSHAKE_FAILED_NO_DETAIL),
HANDSHAKE_SUCCEEDED(ZMQ.HANDSHAKE_SUCCEEDED),
HANDSHAKE_FAILED_PROTOCOL(ZMQ.HANDSHAKE_FAILED_PROTOCOL),
HANDSHAKE_FAILED_AUTH(ZMQ.HANDSHAKE_FAILED_AUTH),
HANDSHAKE_PROTOCOL(ZMQ.EVENT_HANDSHAKE_PROTOCOL),
ALL(ZMQ.EVENT_ALL);
private static final Map MAP = new HashMap<>(Event.values().length);
static {
for (Event e : Event.values()) {
MAP.put(e.code, e);
}
}
private final int code;
Event(int code)
{
this.code = code;
}
/**
* Find the {@link Event} associated with the numerical event code.
* @param event the numerical event code
* @return the found {@link Event}
*/
public static Event findByCode(int event)
{
return MAP.getOrDefault(event, ALL);
}
}
/**
* The code returned by handshake events, as generated by eventHandshakeXXX
.
*
*/
public enum ProtocolCode
{
ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED),
ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND),
ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_SEQUENCE(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_SEQUENCE),
ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE),
ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_UNSPECIFIED(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_UNSPECIFIED),
ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE),
ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO),
ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE),
ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR),
ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY),
ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_WELCOME(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_WELCOME),
ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_METADATA(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_METADATA),
ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC),
ZMQ_PROTOCOL_ERROR_ZMTP_MECHANISM_MISMATCH(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MECHANISM_MISMATCH),
ZMQ_PROTOCOL_ERROR_ZAP_UNSPECIFIED(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_UNSPECIFIED),
ZMQ_PROTOCOL_ERROR_ZAP_MALFORMED_REPLY(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_MALFORMED_REPLY),
ZMQ_PROTOCOL_ERROR_ZAP_BAD_REQUEST_ID(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_BAD_REQUEST_ID),
ZMQ_PROTOCOL_ERROR_ZAP_BAD_VERSION(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_BAD_VERSION),
ZMQ_PROTOCOL_ERROR_ZAP_INVALID_STATUS_CODE(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_INVALID_STATUS_CODE),
ZMQ_PROTOCOL_ERROR_ZAP_INVALID_METADATA(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_INVALID_METADATA),
ZMQ_PROTOCOL_ERROR_WS_UNSPECIFIED(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_WS_UNSPECIFIED);
private static final Map MAP = new HashMap<>(ProtocolCode.values().length);
static {
for (ProtocolCode e : ProtocolCode.values()) {
MAP.put(e.code, e);
}
}
private final int code;
ProtocolCode(int code)
{
this.code = code;
}
/**
* Find the {@link ProtocolCode} associated with the numerical error code send with eventHandshakeXXX
.
* @param code the numerical error code
* @return the found {@link ProtocolCode}
*/
public static ProtocolCode findByCode(int code)
{
if (MAP.containsKey(code)) {
return MAP.get(code);
}
else {
throw new IllegalArgumentException("Protocol code unknown: " + code);
}
}
}
private boolean started;
/**
* Starts the monitoring.
* Event types have to be added before the start, or they will take no effect.
* @return this instance.
*/
public final ZMonitor start()
{
if (started) {
System.out.println("ZMonitor: Unable to start while already started.");
return this;
}
agent.send(START);
agent.recv();
started = true;
return this;
}
/**
* Stops the monitoring and closes the actor.
* When returning from that call, ZMonitor will be no more active.
*/
@Override
public final void close()
{
destroy();
}
/**
* Stops the monitoring and closes the actor.
* When returning from that call, ZMonitor will be no more active.
*/
public final void destroy()
{
agent.send(CLOSE);
exit.awaitSilent();
agent.close();
}
/**
* Sets verbosity of the monitor.
* @param verbose true for monitor to be verbose, otherwise false.
* @return this instance.
*/
public final ZMonitor verbose(boolean verbose)
{
if (started) {
System.out.println("ZMonitor: Unable to change verbosity while already started.");
return this;
}
agent.send(VERBOSE, true);
agent.send(Boolean.toString(verbose));
agent.recv();
return this;
}
/**
* Adds event types to monitor.
* @param events the types of events to monitor.
* @return this instance.
*/
public final ZMonitor add(Event... events)
{
if (started) {
System.out.println("ZMonitor: Unable to add events while already started.");
return this;
}
ZMsg msg = new ZMsg();
msg.add(ADD_EVENTS);
for (Event evt : events) {
msg.add(evt.name());
}
agent.send(msg);
agent.recv();
return this;
}
/**
* Removes event types from monitor.
* @param events the types of events to stop monitoring.
* @return this instance.
*/
public final ZMonitor remove(Event... events)
{
if (started) {
System.out.println("ZMonitor: Unable to remove events while already started.");
return this;
}
ZMsg msg = new ZMsg();
msg.add(REMOVE_EVENTS);
for (Event evt : events) {
msg.add(evt.name());
}
agent.send(msg);
agent.recv();
return this;
}
/**
* Gets the next event, blocking for it until available.
* @return the next monitored event or null if closed.
*/
public final ZEvent nextEvent()
{
return nextEvent(true);
}
/**
* Gets the next event, blocking for it until available if requested.
* @param wait true to block until next event is available, false to immediately return with null if there is no event.
* @return the next event or null if there is currently none.
*/
public final ZEvent nextEvent(boolean wait)
{
if (!started) {
System.out.println("ZMonitor: Start before getting events.");
return null;
}
ZMsg msg = agent.recv(wait);
if (msg == null) {
return null;
}
return new ZEvent(msg);
}
/**
* Gets the next event, blocking for it until available if requested.
* @param timeout the time in milliseconds to wait for a message before returning null, -1 to block.
* @return the next event or null if there is currently none after the specified timeout.
*/
public final ZEvent nextEvent(int timeout)
{
if (!started) {
System.out.println("ZMonitor: Start before getting events.");
return null;
}
ZMsg msg = agent.recv(timeout);
if (msg == null) {
return null;
}
return new ZEvent(msg);
}
private static final String START = "START";
private static final String CLOSE = "CLOSE";
private static final String VERBOSE = "VERBOSE";
private static final String ADD_EVENTS = "ADD_EVENTS";
private static final String REMOVE_EVENTS = "REMOVE_EVENTS";
private final ZAgent agent;
private final ZStar.Exit exit;
/**
* Creates a monitoring actor for the given socket.
* @param ctx the context relative to this actor. Not null.
* @param socket the socket to monitor for events. Not null.
*/
public ZMonitor(ZContext ctx, Socket socket)
{
Objects.requireNonNull(ctx, "ZMonitor works only with a supplied context");
Objects.requireNonNull(socket, "Socket has to be supplied");
final MonitorActor actor = new MonitorActor(socket);
final ZActor zactor = new ZActor(ctx, actor, UUID.randomUUID().toString());
agent = zactor.agent();
exit = zactor.exit();
// wait for the start of the actor
agent.recv().destroy();
}
private static class MonitorActor extends ZActor.SimpleActor
{
private static final String ERROR = "ERROR";
private static final String OK = "OK";
private final Socket monitored; // the monitored socket
private final String address; // the address where events will be collected
private Socket monitor; // the monitoring socket
private int events; // the events to monitor
private boolean verbose;
public MonitorActor(ZMQ.Socket socket)
{
assert (socket != null);
this.monitored = socket;
this.address = String.format("inproc://zmonitor-%s-%s", socket.hashCode(), UUID.randomUUID());
}
@Override
public String premiere(Socket pipe)
{
return "ZMonitor-" + monitored.toString();
}
@Override
public List createSockets(ZContext ctx, Object... args)
{
monitor = ctx.createSocket(SocketType.PAIR);
assert (monitor != null);
return Collections.singletonList(monitor);
}
@Override
public void start(Socket pipe, List sockets, ZPoller poller)
{
pipe.send("STARTED");
}
@Override
public boolean stage(Socket socket, Socket pipe, ZPoller poller, int evts)
{
final zmq.ZMQ.Event event = zmq.ZMQ.Event.read(socket.base());
assert (event != null);
final int code = event.event;
final String address = event.addr;
assert (address != null);
final Event type = Event.findByCode(code);
assert (type != null);
final ZMsg msg = new ZMsg();
final ZFrame frame = new ZFrame(new byte[1 + 4 + address.length() + 4]);
final ZNeedle needle = new ZNeedle(frame);
needle.putNumber1(type.ordinal());
needle.putNumber4(code);
needle.putString(address);
msg.add(frame);
final Object value = event.arg;
if (value != null) {
msg.add(value.toString());
}
return msg.send(pipe, true);
}
@Override
public boolean backstage(ZMQ.Socket pipe, ZPoller poller, int evts)
{
final String command = pipe.recvStr();
if (command == null) {
System.out.printf("ZMonitor: Closing monitor %s : No command%n", monitored);
return false;
}
switch (command) {
case VERBOSE:
verbose = Boolean.parseBoolean(pipe.recvStr());
return pipe.send(OK);
case ADD_EVENTS:
return addEvents(pipe);
case REMOVE_EVENTS:
return removeEvents(pipe);
case START:
return start(poller, pipe);
case CLOSE:
return close(poller, pipe);
default:
System.out.printf("ZMonitor: Closing monitor %s : Unknown command %s%n", monitored, command);
pipe.send(ERROR);
return false;
}
}
private boolean addEvents(Socket pipe)
{
final ZMsg msg = ZMsg.recvMsg(pipe);
if (msg == null) {
return false; // interrupted
}
for (ZFrame frame : msg) {
final String evt = frame.getString(ZMQ.CHARSET);
final Event event = Event.valueOf(evt);
if (verbose) {
System.out.printf("ZMonitor: Adding" + " event %s%n", event);
}
events |= event.code;
}
return pipe.send(OK);
}
private boolean removeEvents(Socket pipe)
{
final ZMsg msg = ZMsg.recvMsg(pipe);
if (msg == null) {
return false; // interrupted
}
for (ZFrame frame : msg) {
final String evt = frame.getString(ZMQ.CHARSET);
final Event event = Event.valueOf(evt);
if (verbose) {
System.out.printf("ZMonitor: Removing" + " event %s%n", event);
}
events &= ~event.code;
}
return pipe.send(OK);
}
private boolean start(ZPoller poller, Socket pipe)
{
boolean rc = true;
String err = "";
if (rc) {
rc = monitored.monitor(address, events);
err = "Unable to monitor socket " + monitored;
}
if (rc) {
err = "Unable to connect monitoring socket " + monitor;
rc = monitor.connect(address);
}
if (rc) {
err = "Unable to poll monitoring socket " + monitor;
rc = poller.register(monitor, ZPoller.IN);
}
log("tart", rc, err);
if (rc) {
return pipe.send(OK);
}
pipe.send(ERROR);
return false;
}
private boolean close(ZPoller poller, Socket pipe)
{
boolean rc = poller.unregister(monitor);
String err = "Unable to unregister monitoring socket " + monitor;
if (rc) {
err = "Unable to stop monitor socket " + monitored;
rc = monitored.monitor(null, events);
}
log("top", rc, err);
if (verbose) {
System.out.printf("ZMonitor: Closing monitor %s%n", monitored);
}
pipe.send(rc ? OK : ERROR);
return false;
}
private void log(String action, boolean rc, String err)
{
if (verbose) {
if (rc) {
System.out.printf("ZMonitor: S%s monitor for events %s on %s%n", action, events, monitored);
}
else {
System.out.printf(
"ZMonitor: Unable to s%s monitor for events %s (%s) on %s%n",
action,
events,
err,
monitored);
}
}
}
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZMsg.java 0000664 0000000 0000000 00000043013 14557711263 0021311 0 ustar 00root root 0000000 0000000 package org.zeromq;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Deque;
import java.util.Iterator;
import java.util.Objects;
import java.util.stream.Collectors;
import org.zeromq.ZMQ.Socket;
import zmq.util.Draft;
import zmq.util.function.Consumer;
/**
* The ZMsg class provides methods to send and receive multipart messages
* across 0MQ sockets. This class provides a list-like container interface,
* with methods to work with the overall container. ZMsg messages are
* composed of zero or more ZFrame objects.
*
*
* // Send a simple single-frame string message on a ZMQSocket "output" socket object
* ZMsg.newStringMsg("Hello").send(output);
*
* // Add several frames into one message
* ZMsg msg = new ZMsg();
* for (int i = 0 ; i < 10 ; i++) {
* msg.addString("Frame" + i);
* }
* msg.send(output);
*
* // Receive message from ZMQSocket "input" socket object and iterate over frames
* ZMsg receivedMessage = ZMsg.recvMsg(input);
* for (ZFrame f : receivedMessage) {
* // Do something with frame f (of type ZFrame)
* }
*
*
* Based on zmsg.c in czmq
*
*/
public class ZMsg implements Iterable, Deque
{
/**
* Hold internal list of ZFrame objects
*/
private final ArrayDeque frames = new ArrayDeque<>();
/**
* Class Constructor
*/
public ZMsg()
{
}
/**
* Destructor.
* Explicitly destroys all ZFrames contains in the ZMsg
*/
public void destroy()
{
for (ZFrame f : frames) {
f.destroy();
}
frames.clear();
}
/**
* @return total number of bytes contained in all ZFrames in this ZMsg
*/
public long contentSize()
{
long size = 0;
for (ZFrame f : frames) {
size += f.size();
}
return size;
}
/**
* Add a String as a new ZFrame to the end of list
* @param str
* String to add to list
*/
public ZMsg addString(String str)
{
frames.add(new ZFrame(str));
return this;
}
/**
* Creates copy of this ZMsg.
* Also duplicates all frame content.
* @return
* The duplicated ZMsg object, else null if this ZMsg contains an empty frame set
*/
public ZMsg duplicate()
{
if (frames.isEmpty()) {
return null;
}
else {
ZMsg msg = new ZMsg();
for (ZFrame f : frames) {
msg.add(f.duplicate());
}
return msg;
}
}
/**
* Push frame plus empty frame to front of message, before 1st frame.
* Message takes ownership of frame, will destroy it when message is sent.
* @param frame
*/
public ZMsg wrap(ZFrame frame)
{
if (frame != null) {
push(new ZFrame(""));
push(frame);
}
return this;
}
/**
* Pop frame off front of message, caller now owns frame.
* If next frame is empty, pops and destroys that empty frame
* (e.g. useful when unwrapping ROUTER socket envelopes)
* @return
* Unwrapped frame
*/
public ZFrame unwrap()
{
if (size() == 0) {
return null;
}
ZFrame f = pop();
ZFrame empty = getFirst();
if (empty.hasData() && empty.size() == 0) {
empty = pop();
empty.destroy();
}
return f;
}
/**
* Send message to 0MQ socket.
*
* @param socket
* 0MQ socket to send ZMsg on.
* @return true if send is success, false otherwise
*/
public boolean send(Socket socket)
{
return send(socket, true);
}
/**
* Send message to 0MQ socket, destroys contents after sending if destroy param is set to true.
* If the message has no frames, sends nothing but still destroy()s the ZMsg object
* @param socket
* 0MQ socket to send ZMsg on.
* @return true if send is success, false otherwise
*/
public boolean send(Socket socket, boolean destroy)
{
if (socket == null) {
throw new IllegalArgumentException("socket is null");
}
if (frames.isEmpty()) {
return true;
}
boolean ret = true;
Iterator i = frames.iterator();
while (i.hasNext()) {
ZFrame f = i.next();
ret = f.sendAndKeep(socket, (i.hasNext()) ? ZFrame.MORE : 0);
if (!ret) {
break;
}
}
if (destroy) {
destroy();
}
return ret;
}
/**
* Receives message from socket, returns ZMsg object or null if the
* recv was interrupted. Does a blocking recv, if you want not to block then use
* the ZLoop class or ZMQ.Poller to check for socket input before receiving or recvMsg with flag ZMQ.DONTWAIT.
* @param socket
* @return
* ZMsg object, null if interrupted
*/
public static ZMsg recvMsg(Socket socket)
{
return recvMsg(socket, 0);
}
/**
* Receives message from socket, returns ZMsg object or null if the
* recv was interrupted.
* @param socket
* @param wait true to wait for next message, false to do a non-blocking recv.
* @return
* ZMsg object, null if interrupted
*/
public static ZMsg recvMsg(Socket socket, boolean wait)
{
return recvMsg(socket, wait ? 0 : ZMQ.DONTWAIT);
}
/**
* Receives message from socket, returns ZMsg object or null if the
* recv was interrupted. Setting the flag to ZMQ.DONTWAIT does a non-blocking recv.
* @param socket
* @param flag see ZMQ constants
* @return
* ZMsg object, null if interrupted
*/
public static ZMsg recvMsg(Socket socket, int flag)
{
if (socket == null) {
throw new IllegalArgumentException("socket is null");
}
ZMsg msg = new ZMsg();
while (true) {
ZFrame f = ZFrame.recvFrame(socket, flag);
if (f == null) {
// If receive failed or was interrupted
msg.destroy();
msg = null;
break;
}
msg.add(f);
if (!f.hasMore()) {
break;
}
}
return msg;
}
/**
* This API is in DRAFT state and is subject to change at ANY time until declared stable
* handle incoming message with a handler
*
* @param socket
* @param flags see ZMQ constants
* @param handler handler to handle incoming message
* @param exceptionHandler handler to handle exceptions
*/
@Draft
public static void recvMsg(ZMQ.Socket socket, int flags,
Consumer handler,
Consumer exceptionHandler)
{
try {
handler.accept(ZMsg.recvMsg(socket, flags));
}
catch (ZMQException e) {
exceptionHandler.accept(e);
}
}
/**
* This API is in DRAFT state and is subject to change at ANY time until declared stable
* handle incoming message with a handler
*
* @param socket
* @param flags see ZMQ constants
* @param handler handler to handle incoming message
*/
@Draft
public static void recvMsg(ZMQ.Socket socket, int flags, Consumer handler)
{
handler.accept(ZMsg.recvMsg(socket, flags));
}
/**
* Save message to an open data output stream.
*
* Data saved as:
* 4 bytes: number of frames
* For every frame:
* 4 bytes: byte size of frame data
* + n bytes: frame byte data
*
* @param msg
* ZMsg to save
* @param file
* DataOutputStream
* @return
* True if saved OK, else false
*/
public static boolean save(ZMsg msg, DataOutputStream file)
{
if (msg == null) {
return false;
}
try {
// Write number of frames
file.writeInt(msg.size());
if (msg.size() > 0) {
for (ZFrame f : msg) {
// Write byte size of frame
file.writeInt(f.size());
// Write frame byte data
file.write(f.getData());
}
}
return true;
}
catch (IOException e) {
return false;
}
}
/**
* Load / append a ZMsg from an open DataInputStream
*
* @param file
* DataInputStream connected to file
* @return
* ZMsg object
*/
public static ZMsg load(DataInputStream file)
{
if (file == null) {
return null;
}
ZMsg rcvMsg = new ZMsg();
try {
int msgSize = file.readInt();
if (msgSize > 0) {
int msgNbr = 0;
while (++msgNbr <= msgSize) {
int frameSize = file.readInt();
byte[] data = new byte[frameSize];
file.read(data);
rcvMsg.add(new ZFrame(data));
}
}
return rcvMsg;
}
catch (IOException e) {
return null;
}
}
/**
* Create a new ZMsg from one or more Strings
*
* @param strings
* Strings to add as frames.
* @return
* ZMsg object
*/
public static ZMsg newStringMsg(String... strings)
{
ZMsg msg = new ZMsg();
for (String data : strings) {
msg.addString(data);
}
return msg;
}
@Override
public boolean equals(Object o)
{
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ZMsg zMsg = (ZMsg) o;
//based on AbstractList
Iterator e1 = frames.iterator();
Iterator e2 = zMsg.frames.iterator();
while (e1.hasNext() && e2.hasNext()) {
ZFrame o1 = e1.next();
ZFrame o2 = e2.next();
if (!(Objects.equals(o1, o2))) {
return false;
}
}
return !(e1.hasNext() || e2.hasNext());
}
@Override
public int hashCode()
{
if (frames.isEmpty()) {
return 0;
}
int result = 1;
for (ZFrame frame : frames) {
result = 31 * result + (frame == null ? 0 : frame.hashCode());
}
return result;
}
/**
* Dump the message in human readable format. This should only be used
* for debugging and tracing, inefficient in handling large messages.
**/
public ZMsg dump(Appendable out)
{
try {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
pw.printf("--------------------------------------\n");
for (ZFrame frame : frames) {
pw.printf("[%03d] %s\n", frame.size(), frame);
}
out.append(sw.getBuffer());
sw.close();
}
catch (IOException e) {
throw new RuntimeException("Message dump exception " + super.toString(), e);
}
return this;
}
public ZMsg dump()
{
dump(System.out);
return this;
}
// ********* Convenience Deque methods for common data types *** //
public ZMsg addFirst(String stringValue)
{
addFirst(new ZFrame(stringValue));
return this;
}
public ZMsg addFirst(byte[] data)
{
addFirst(new ZFrame(data));
return this;
}
public ZMsg addLast(String stringValue)
{
addLast(new ZFrame(stringValue));
return this;
}
public ZMsg addLast(byte[] data)
{
addLast(new ZFrame(data));
return this;
}
// ********* Convenience Queue methods for common data types *** //
public ZMsg push(String str)
{
push(new ZFrame(str));
return this;
}
public ZMsg push(byte[] data)
{
push(new ZFrame(data));
return this;
}
public boolean add(String stringValue)
{
return add(new ZFrame(stringValue));
}
public boolean add(byte[] data)
{
return add(new ZFrame(data));
}
/**
* Adds a string as a new frame in the message.
*
* @param stringValue the value to add
* @return this
*/
public ZMsg append(String stringValue)
{
add(stringValue);
return this;
}
/**
* Adds bytes as a new frame in the message.
*
* @param data the value to add
* @return this
*/
public ZMsg append(byte[] data)
{
add(data);
return this;
}
// ********* Implement Iterable Interface *************** //
@Override
public Iterator iterator()
{
return frames.iterator();
}
// ********* Implement Deque Interface ****************** //
@Override
public boolean addAll(Collection extends ZFrame> arg0)
{
return frames.addAll(arg0);
}
@Override
public void clear()
{
frames.clear();
}
@Override
public boolean containsAll(Collection> arg0)
{
return frames.containsAll(arg0);
}
@Override
public boolean isEmpty()
{
return frames.isEmpty();
}
@Override
public boolean removeAll(Collection> arg0)
{
return frames.removeAll(arg0);
}
@Override
public boolean retainAll(Collection> arg0)
{
return frames.retainAll(arg0);
}
@Override
public Object[] toArray()
{
return frames.toArray();
}
@Override
public T[] toArray(T[] arg0)
{
return frames.toArray(arg0);
}
@Override
public boolean add(ZFrame e)
{
return frames.add(e);
}
@Override
public void addFirst(ZFrame e)
{
frames.addFirst(e);
}
@Override
public void addLast(ZFrame e)
{
frames.addLast(e);
}
@Override
public boolean contains(Object o)
{
return frames.contains(o);
}
@Override
public Iterator descendingIterator()
{
return frames.descendingIterator();
}
@Override
public ZFrame element()
{
return frames.element();
}
@Override
public ZFrame getFirst()
{
return frames.peekFirst();
}
@Override
public ZFrame getLast()
{
return frames.peekLast();
}
@Override
public boolean offer(ZFrame e)
{
return frames.offer(e);
}
@Override
public boolean offerFirst(ZFrame e)
{
return frames.offerFirst(e);
}
@Override
public boolean offerLast(ZFrame e)
{
return frames.offerLast(e);
}
@Override
public ZFrame peek()
{
return frames.peek();
}
@Override
public ZFrame peekFirst()
{
return frames.peekFirst();
}
@Override
public ZFrame peekLast()
{
return frames.peekLast();
}
@Override
public ZFrame poll()
{
return frames.poll();
}
@Override
public ZFrame pollFirst()
{
return frames.pollFirst();
}
@Override
public ZFrame pollLast()
{
return frames.pollLast();
}
@Override
public ZFrame pop()
{
return frames.poll();
}
/**
* Pop a ZFrame and return the toString() representation of it.
*
* @return toString version of pop'ed frame, or null if no frame exists.
*/
public String popString()
{
ZFrame frame = pop();
if (frame == null) {
return null;
}
return frame.toString();
}
@Override
public void push(ZFrame e)
{
frames.push(e);
}
@Override
public ZFrame remove()
{
return frames.remove();
}
@Override
public boolean remove(Object o)
{
return frames.remove(o);
}
@Override
public ZFrame removeFirst()
{
return frames.pollFirst();
}
@Override
public boolean removeFirstOccurrence(Object o)
{
return frames.removeFirstOccurrence(o);
}
@Override
public ZFrame removeLast()
{
return frames.pollLast();
}
@Override
public boolean removeLastOccurrence(Object o)
{
return frames.removeLastOccurrence(o);
}
@Override
public int size()
{
return frames.size();
}
public ZMsg append(ZMsg msg)
{
if (msg == null) {
return this;
}
// Tests explicitly check appending a ZMsg to itself, protect that
if (msg != this) {
frames.addAll(msg.frames);
}
else {
frames.addAll(msg.frames.clone());
}
return this;
}
/**
* Returns pretty string representation of multipart message:
* [ frame0, frame1, ..., frameN ]
*
* @return toString version of ZMsg object
*/
@Override
public String toString()
{
String joined = frames.stream().map(ZFrame::toString).collect(Collectors.joining(", "));
return new StringBuilder("[ ").append(joined).append(" ]").toString();
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZPoller.java 0000664 0000000 0000000 00000077605 14557711263 0022036 0 ustar 00root root 0000000 0000000 package org.zeromq;
import java.io.Closeable;
import java.nio.channels.SelectableChannel;
import java.nio.channels.Selector;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import org.zeromq.ZMQ.Poller;
import org.zeromq.ZMQ.Socket;
import zmq.poll.PollItem;
import zmq.util.Objects;
import zmq.util.function.BiFunction;
import zmq.util.function.Function;
/**
* Rewritten poller for ØMQ.
*
* Polls selectable channels and sockets for specified events.
*
* This poller can be used in two ways:
*
*
* the traditional one, where you make something like
*
* {@code
ZPoller poller = ...
poller.register(socket, ZPoller.POLLIN);
poller.register(channel, ZPoller.OUT);
int events = poller.poll(-1L);
if (poller.isReadable(socket)) {
...
}
if (poller.writable(channel)) {
...
}
}
* the event-driven way
*
* {@code
*
ZPoller poller = ...
poller.setGlobalHandler(...)
ZPoller.EventsHandler handler = ...
// The events method of the handler will be called
poller.register(channel, handler, ZPoller.IN);
// The events method of the global handler will be called
poller.register(socket, ZPoller.POLLOUT);
poller.poll(-1L);
// handlers have been called
}
* The motivations of this rewriting are:
*
* the bare poller used {@link zmq.ZMQ#poll(Selector, PollItem[], int, long)}. This method did not allow
* to choose the selector used for polling, relying on a ThreadLocal, which is dangerous.
* the bare poller use algorithms tailored for languages with manual allocation.
* No need here as Java allows more flexibility. TODO There still may be a small penalty cost.
*
*
* It’s thread safe, but newly registered items will only be handled at the next poll iteration,
* so if {@code poller.poll(-1L)} is used, adding items will have no effect. The situation is the same with the global handler
*/
// Poller for 0MQ.
public class ZPoller implements Closeable
{
// contract for events
public interface EventsHandler
{
/**
* Called when the poller intercepts events.
*
* @param socket the socket with events
* @param events the interesting events as an ORed combination of IN, OUT, ERR
* @return true to continue the polling , false to stop it
*/
boolean events(Socket socket, int events);
/**
* Called when the poller intercepts events.
*
* @param channel the channel with events
* @param events the interesting events as an ORed combination of IN, OUT, ERR
* @return true to continue the polling , false to stop it
*/
boolean events(SelectableChannel channel, int events);
}
public static class ComposeEventsHandler implements EventsHandler
{
private final BiFunction sockets;
private final BiFunction channels;
public ComposeEventsHandler(BiFunction sockets,
BiFunction channels)
{
super();
this.sockets = sockets;
this.channels = channels;
}
@Override
public boolean events(Socket socket, int events)
{
assert (sockets != null) : "A Socket Handler needs to be set";
return sockets.apply(socket, events);
}
@Override
public boolean events(SelectableChannel channel, int events)
{
assert (channels != null) : "A SelectableChannel Handler needs to be set";
return channels.apply(channel, events);
}
}
// contract for items. Useful for providing own implementation.
public interface ItemHolder
{
// the inner ZMQ poll item
PollItem item();
// the related ZeroMQ socket
Socket socket();
// the associated events handler
EventsHandler handler();
}
// contract for items creation. Useful for delegating.
public interface ItemCreator
{
/**
* Creates a new holder for a poll item.
*
* @param socket the socket to poll
* @param handler the optional handler for polled events
* @param events the interested events
* @return a new poll item holder
*/
ItemHolder create(Socket socket, EventsHandler handler, int events);
/**
* Creates a new holder for a poll item.
*
* @param channel the channel to poll
* @param handler the optional handler for polled events.
* @param events the interested events
* @return a new poll item holder
*/
ItemHolder create(SelectableChannel channel, EventsHandler handler, int events);
}
// re-re-implementation of poll item
public static class ZPollItem extends ZMQ.PollItem implements ItemHolder
{
private final EventsHandler handler;
public ZPollItem(final Socket socket, final EventsHandler handler, final int ops)
{
super(socket, ops);
this.handler = handler;
assert (item() != null);
}
public ZPollItem(final SelectableChannel channel, final EventsHandler handler, final int ops)
{
super(channel, ops);
this.handler = handler;
assert (item() != null);
}
@Override
public PollItem item()
{
return base();
}
@Override
public Socket socket()
{
return getSocket();
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((item() == null) ? 0 : item().hashCode());
result = prime * result + ((getRawSocket() == null) ? 0 : getRawSocket().hashCode());
result = prime * result + ((socket() == null) ? 0 : socket().hashCode());
result = prime * result + ((handler() == null) ? 0 : handler().hashCode());
return result;
}
@Override
public boolean equals(final Object obj)
{
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof ItemHolder)) {
return false;
}
ItemHolder other = (ItemHolder) obj;
// this.item() is never null
if (other.item() == null) {
return false;
}
// no checking on item() as zmq.PollItem does not override equals()
if (item().getRawSocket() == null) {
if (other.item().getRawSocket() != null) {
return false;
}
}
else if (!item().getRawSocket().equals(other.item().getRawSocket())) {
return false;
}
if (socket() == null) {
if (other.socket() != null) {
return false;
}
}
else if (!socket().equals(other.socket())) {
return false;
}
if (handler() == null) {
return other.handler() == null;
}
else {
return handler().equals(other.handler());
}
}
@Override
public EventsHandler handler()
{
return handler;
}
}
private class CompositePollItem implements ItemHolder, EventsHandler
{
private final Set holders = ConcurrentHashMap.newKeySet();
private final Socket socket;
private final SelectableChannel channel;
private PollItem item;
public CompositePollItem(final Object socketOrChannel)
{
this.socket = socketOrChannel instanceof Socket ? (Socket) socketOrChannel : null;
this.channel = socketOrChannel instanceof SelectableChannel ? (SelectableChannel) socketOrChannel : null;
assert (socket != null || channel != null);
}
@Override
public PollItem item()
{
if (item == null) {
item = createItem();
}
return item;
}
private PollItem createItem()
{
if (socket == null) {
return new PollItem(channel, ops());
}
else {
return new PollItem(socket.base(), ops());
}
}
private int ops()
{
int ops = 0;
for (ItemHolder holder : holders) {
int interest = holder.item().zinterestOps();
ops |= interest;
}
return ops;
}
@Override
public Socket socket()
{
return socket;
}
@Override
public EventsHandler handler()
{
return this;
}
@Override
public boolean events(Socket socket, int events)
{
return doEvent(h -> h.events(socket, events), events);
}
@Override
public boolean events(SelectableChannel channel, int events)
{
return doEvent(h -> h.events(channel, events), events);
}
private boolean doEvent(Function handle, int events)
{
boolean has = false;
boolean first = true;
for (ItemHolder holder : holders) {
if (holder.item().hasEvent(events)) {
if (first) {
first = false;
has = true;
}
EventsHandler handler = holder.handler() == null ? currentGlobalHandler.get() : holder.handler();
if (handler != null) {
has &= handle.apply(handler);
}
}
}
return has;
}
}
/******************************************************************************/
/* 0MQ socket events */
/******************************************************************************/
/*
* These values can be ORed to specify what we want to poll for.
*/
public static final int POLLIN = Poller.POLLIN;
public static final int POLLOUT = Poller.POLLOUT;
public static final int POLLERR = Poller.POLLERR;
// same values with shorter writing
public static final int IN = POLLIN;
public static final int OUT = POLLOUT;
public static final int ERR = POLLERR;
// same values with semantic consistency
public static final int READABLE = POLLIN;
public static final int WRITABLE = POLLOUT;
/**
* Creates a new poller based on the current one.
* This will be a shadow poller, sharing the same selector and items creator.
* The global events handler will not be shared.
*
* @param poller the main poller.
*/
public ZPoller(final ZPoller poller)
{
this(poller.creator, poller.selector);
}
/**
* Creates a new poller with a given selector for operational polling.
*
* @param selector the selector to use for polling.
*/
public ZPoller(final Selector selector)
{
this(new SimpleCreator(), selector);
}
/**
* Creates a new poller attached to a given context that will provide
* selector for operational polling.
*
* @param context
* the context that will provide the selector to use for polling.
*/
public ZPoller(final ZContext context)
{
this(new SimpleCreator(), context);
}
/**
* Creates a new poller based on the current one. This will be a shadow
* poller, sharing the same selector. The global events handler will not be
* shared.
*
* @param creator the items creator
* @param poller the main poller.
*/
public ZPoller(final ItemCreator creator, final ZPoller poller)
{
this(creator, poller.selector);
}
/**
* Creates a new poller attached to a given context that will provide
* selector for operational polling.
*
* @param creator
* the items creator
* @param context
* the context that will provide the selector to use for polling.
*/
public ZPoller(final ItemCreator creator, final ZContext context)
{
this(creator, context.selector());
}
/**
* Creates a new poller.
*
* @param creator the items creator
* @param selector the selector to use for polling.
*/
private ZPoller(final ItemCreator creator, final Selector selector)
{
Objects.requireNonNull(creator, "Item creator is mandatory for ZPoller");
Objects.requireNonNull(selector, "Selector is mandatory for ZPoller");
this.creator = creator;
this.selector = selector;
items = new ConcurrentHashMap<>();
}
// creates a new poll item
protected ItemHolder create(final Socket socket, final EventsHandler handler, final int events)
{
Objects.requireNonNull(socket, "Socket has to be non-null");
return creator.create(socket, handler, events);
}
// creates a new poll item
protected ItemHolder create(final SelectableChannel channel, final EventsHandler handler, final int events)
{
Objects.requireNonNull(channel, "Channel has to be non-null");
return creator.create(channel, handler, events);
}
/**
* Sets the global events handler for all registered sockets.
*
* @param globalHandler the events handler to set
*/
public void setGlobalHandler(final EventsHandler globalHandler)
{
this.globalHandler.set(globalHandler);
}
/**
* Returns the global events handler for all registered sockets.
*
* @return the global events handler for all registered sockets.
*/
public EventsHandler getGlobalHandler()
{
return globalHandler.get();
}
/**
* Register a Socket for polling on specified events.
*
* @param socket the registering socket.
* @param handler the events handler for this socket
* @param events the events to listen to, as a mask composed by ORing POLLIN, POLLOUT and POLLERR.
* @return true if registered, otherwise false
*/
public final boolean register(final Socket socket, final BiFunction handler,
final int events)
{
return register(socket, new ComposeEventsHandler(handler, null), events);
}
/**
* Register a Socket for polling on specified events.
*
* @param socket the registering socket.
* @param handler the events handler for this socket
* @param events the events to listen to, as a mask composed by ORing POLLIN, POLLOUT and POLLERR.
* @return true if registered, otherwise false
*/
public final boolean register(final Socket socket, final EventsHandler handler, final int events)
{
if (socket == null) {
return false;
}
return add(socket, create(socket, handler, events));
}
/**
* Register a Socket for polling on all events.
*
* @param socket the registering socket.
* @param handler the events handler for this socket
* @return true if registered, otherwise false
*/
public final boolean register(final Socket socket, final EventsHandler handler)
{
return register(socket, handler, POLLIN | POLLOUT | POLLERR);
}
/**
* Register a Socket for polling on specified events, using the global {@link EventsHandler}.
*
* @param socket the registering socket.
* @param events the events to listen to, as a mask composed by ORing POLLIN, POLLOUT and POLLERR.
* @return true if registered, otherwise false
*/
public final boolean register(final Socket socket, final int events)
{
return register(socket, globalHandler.get(), events);
}
/**
* Registers a SelectableChannel for polling on specified events.
*
* @param channel the registering channel.
* @param handler the events handler for this channel
* @param events the events to listen to, as a mask composed by ORing POLLIN, POLLOUT and POLLERR.
* @return true if registered, otherwise false
*/
public final boolean register(final SelectableChannel channel,
final BiFunction handler, final int events)
{
return register(channel, new ComposeEventsHandler(null, handler), events);
}
/**
* Registers a SelectableChannel for polling on specified events.
*
* @param channel the registering channel.
* @param handler the events handler for this channel
* @param events the events to listen to, as a mask composed by ORing POLLIN, POLLOUT and POLLERR.
* @return true if registered, otherwise false
*/
public final boolean register(final SelectableChannel channel, final EventsHandler handler, final int events)
{
if (channel == null) {
return false;
}
return add(channel, create(channel, handler, events));
}
/**
* Registers a SelectableChannel for polling on all events.
*
* @param channel the registering channel.
* @param handler the events handler for this channel
* @return true if registered, otherwise false
*/
public final boolean register(final SelectableChannel channel, final EventsHandler handler)
{
return register(channel, handler, IN | OUT | ERR);
}
/**
* Registers a SelectableChannel for polling on specified events.
*
* @param channel the registering channel.
* @param events the events to listen to, as a mask composed by ORing POLLIN, POLLOUT and POLLERR.
* @return true if registered, otherwise false
*/
public final boolean register(final SelectableChannel channel, final int events)
{
return register(channel, globalHandler.get(), events);
}
/**
*
* @return the number of registered object, mixing Sockets and SelectableChannels
*/
public int registered()
{
return items.size();
}
/**
* Register an ItemHolder for polling on specified events.
*
* @param item the registering item.
* @return true if registered, otherwise false
*/
public final boolean register(final ItemHolder item)
{
return add(null, item);
}
/**
* Unregister a Socket or SelectableChannel for polling on the specified events.
*
* @param socketOrChannel the Socket or SelectableChannel to be unregistered
* @return true if unregistered, otherwise false
* TODO would it be useful to unregister only for specific events ?
*/
public final synchronized boolean unregister(final Object socketOrChannel)
{
if (socketOrChannel == null) {
return false;
}
CompositePollItem removedItems = items.remove(socketOrChannel);
return removedItems != null;
}
/******************************************************************************/
/* POLLING | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |*/
/******************************************************************************/
/**
* Issue a poll call, using the specified timeout value.
*
* Since ZeroMQ 3.0, the timeout parameter is in milliseconds ,
* but prior to this the unit was microseconds .
*
If timeout is -1 and there is no object registered, it will return immediately
*
* @param timeout
* the timeout, as per zmq_poll ();
* if -1, it will block indefinitely until an event
* happens; if 0, it will return immediately;
* otherwise, it will wait for at most that many
* milliseconds/microseconds (see above).
* @see "http://api.zeromq.org/3-0:zmq-poll"
* @return how many objects where signaled by poll ()
*/
public int poll(final long timeout)
{
return poll(timeout, true);
}
/**
* Issue a poll call, using the specified timeout value.
* If timeout is -1 and there is no object registered, it will return immediately
*
* @param timeout the timeout, as per zmq_poll ();
* @param dispatchEvents true to dispatch events using items handler and the global one.
* @see "http://api.zeromq.org/3-0:zmq-poll"
* @return how many objects where signaled by poll ()
*/
protected int poll(final long timeout, final boolean dispatchEvents)
{
// Local copy of the items, for consistency
Set allPolled = new HashSet<>(items.values());
// get all the raw items
final Set pollItems = allPolled.stream()
.map(CompositePollItem::item)
.collect(Collectors.toSet());
// polling time
final int rc = poll(selector, timeout, pollItems);
if (!dispatchEvents) {
// raw result
return rc;
}
if (dispatch(allPolled)) {
// returns event counts after dispatch if everything went fine
return rc;
}
// error in dispatching
return -1;
}
// does the effective polling
protected int poll(final Selector selector, final long tout, final Collection items)
{
return zmq.ZMQ.poll(selector, items.toArray(new PollItem[0]), items.size(), tout);
}
private boolean dispatch(Set allPolled)
{
return subDispatch(allPolled);
}
/**
* Dispatches the polled events.
*
* @param all the items used for dispatching
* @param size Unused
* @return true if correctly dispatched, false in case of error
* @deprecated Uses {@link #dispatch(Collection)} instead, as size is unused
*/
@SuppressWarnings("unused")
@Deprecated
protected boolean dispatch(final Collection extends ItemHolder> all, int size)
{
return subDispatch(all);
}
/**
* Dispatches the polled events.
*
* @param allDispatched the items used for dispatching
* @return true if correctly dispatched, false in case of error
*/
protected boolean dispatch(final Collection extends ItemHolder> allDispatched)
{
return subDispatch(allDispatched);
}
/**
* dispatches all the polled events to their respective handlers
*
* @return true if correctly dispatched, false in case of error
*/
public boolean dispatch()
{
return subDispatch(new HashSet<>(items.values()));
}
private boolean subDispatch(final Collection extends ItemHolder> allDispatched)
{
EventsHandler localGlobalHandler = currentGlobalHandler.get();
for (ItemHolder holder : allDispatched) {
EventsHandler handler = holder.handler();
if (handler == null) {
handler = localGlobalHandler;
}
if (handler == null) {
// no handler, short-circuit
continue;
}
final PollItem item = holder.item();
final int events = item.readyOps();
if (events <= 0) {
// no events, short-circuit
continue;
}
final Socket socket = holder.socket();
final SelectableChannel channel = holder.item().getRawSocket();
if (socket != null) {
assert (channel == null);
// dispatch on socket
if (!handler.events(socket, events)) {
return false;
}
}
// dispatch on channel
if (channel != null && !handler.events(channel, events)) {
return false;
}
}
// Release the global handler, so it can be refreshed
currentGlobalHandler.remove();
return true;
}
/******************************************************************************/
/*| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | POLLING */
/******************************************************************************/
/**
* Tells if a channel is readable from this poller.
*
* @param channel the channel to ask for.
* @return true if readable, otherwise false
*/
public boolean isReadable(final SelectableChannel channel)
{
return readable((Object) channel);
}
public boolean readable(final SelectableChannel channel)
{
return readable((Object) channel);
}
/**
* Tells if a socket is readable from this poller.
*
* @param socket the socket to ask for.
* @return true if readable, otherwise false
*/
public boolean isReadable(final Socket socket)
{
return readable(socket);
}
public boolean readable(final Socket socket)
{
return readable((Object) socket);
}
// checks for read event
public boolean readable(final Object socketOrChannel)
{
final PollItem it = filter(socketOrChannel, READABLE);
if (it == null) {
return false;
}
return it.isReadable();
}
public boolean pollin(final Socket socket)
{
return isReadable(socket);
}
public boolean pollin(final SelectableChannel channel)
{
return isReadable(channel);
}
/**
* Tells if a channel is writable from this poller.
*
* @param channel the channel to ask for.
* @return true if writable, otherwise false
*/
public boolean isWritable(final SelectableChannel channel)
{
return writable((Object) channel);
}
public boolean writable(final SelectableChannel channel)
{
return writable((Object) channel);
}
/**
* Tells if a socket is writable from this poller.
*
* @param socket the socket to ask for.
* @return true if writable, otherwise false
*/
public boolean isWritable(final Socket socket)
{
return writable((Object) socket);
}
public boolean writable(final Socket socket)
{
return writable((Object) socket);
}
// checks for write event
public boolean writable(final Object socketOrChannel)
{
final PollItem it = filter(socketOrChannel, WRITABLE);
if (it == null) {
return false;
}
return it.isWritable();
}
public boolean pollout(final Socket socket)
{
return isWritable(socket);
}
public boolean pollout(final SelectableChannel channel)
{
return isWritable(channel);
}
/**
* Tells if a channel is in error from this poller.
*
* @param channel the channel to ask for.
* @return true if in error, otherwise false
*/
public boolean isError(final SelectableChannel channel)
{
return error((Object) channel);
}
public boolean error(final SelectableChannel channel)
{
return error((Object) channel);
}
/**
* Tells if a socket is in error from this poller.
*
* @param socket the socket to ask for.
* @return true if in error, otherwise false
*/
public boolean isError(final Socket socket)
{
return error((Object) socket);
}
public boolean error(final Socket socket)
{
return error((Object) socket);
}
// checks for error event
public boolean error(final Object socketOrChannel)
{
final PollItem it = filter(socketOrChannel, ERR);
if (it == null) {
return false;
}
return it.isError();
}
public boolean pollerr(final Socket socket)
{
return isError(socket);
}
public boolean pollerr(final SelectableChannel channel)
{
return isError(channel);
}
/**
* Destroys the poller. Does actually nothing.
*/
@Override
public void close()
{
destroy();
}
/**
* Destroys the poller without exception.
*/
public void destroy()
{
// Nothing to do
}
// selector used for polling
private final Selector selector;
// creator of items
private final ItemCreator creator;
// managed items
private final Map items;
// TODO set of handlers, each with its specified events?
// optional global events handler
private final AtomicReference globalHandler = new AtomicReference<>(null);
// Use to cache the global handler for the current thread
private final ThreadLocal currentGlobalHandler = ThreadLocal.withInitial(globalHandler::get);
// simple creator for poll items
public static class SimpleCreator implements ItemCreator
{
@Override
public ItemHolder create(final Socket socket, final EventsHandler handler, final int events)
{
return new ZPollItem(socket, handler, events);
}
@Override
public ItemHolder create(final SelectableChannel channel, final EventsHandler handler, final int events)
{
return new ZPollItem(channel, handler, events);
}
}
// add an item to this poller
protected boolean add(final Object socketOrChannel, final ItemHolder holder)
{
Object key = Optional.ofNullable(socketOrChannel).orElseGet(() -> computeSocketOrChannel(holder));
CompositePollItem aggregate = items.computeIfAbsent(key, CompositePollItem::new);
return aggregate.holders.add(holder);
}
private Object computeSocketOrChannel(final ItemHolder holder)
{
Socket socket = holder.socket();
SelectableChannel ch = holder.item().getRawSocket();
if (ch == null && socket == null) {
throw new IllegalArgumentException("Needs a socket or a channel to register");
}
else if (socket != null) {
return socket;
}
else {
return ch;
}
}
/**
* Return a new HashSet
* @param size the initial capacity of the hash table
* @return a new hash set
* @deprecated useless
*/
@Deprecated
protected Set createContainer(int size)
{
return new HashSet<>(size);
}
/**
* Return a copy of all the items of this poller.
*/
protected Collection items()
{
return new HashSet<>(items.values());
}
// gets all the items of this poller regarding the given input
protected Iterable items(final Object socketOrChannel)
{
if (socketOrChannel == null || ! items.containsKey(socketOrChannel)) {
return Collections.emptySet();
}
else {
return items.get(socketOrChannel).holders;
}
}
// filters items to get the first one matching the criteria, or null if none found
protected PollItem filter(final Object socketOrChannel, final int events)
{
return Optional.ofNullable(socketOrChannel)
.map(items::get)
.map(CompositePollItem::item)
.filter(pi -> pi.hasEvent(events))
.orElse(null);
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZProxy.java 0000664 0000000 0000000 00000125311 14557711263 0021706 0 ustar 00root root 0000000 0000000 package org.zeromq;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.zeromq.ZActor.Actor;
import org.zeromq.ZAgent.SelectorCreator;
import org.zeromq.ZMQ.Socket;
import org.zeromq.ZStar.Exit;
import zmq.Msg;
import zmq.SocketBase;
/**
* Implementation of a remotely controlled proxy for 0MQ, using {@link ZActor}.
*
* The goals of this implementation are to delegate the creation of sockets
* in a background thread via a callback interface to ensure their correct use
* and to provide ultimately to end-users the following features.
*
* Basic features:
*
* Remote Control
*
* Start: if was paused, flushes the pending messages
* Pause: lets the socket queues accumulate messages according to their types
* Stop: Shutdowns the proxy, can be restarted
* Status: Retrieves the status of the proxy
* Cold Restart: Closes and recreates the connections
* {@link #restart(ZMsg) Hot Restart}: User-defined behavior with custom messages
* {@link #configure(ZMsg) Configure}: User-defined behavior with custom messages
* {@link #command(String, boolean)} ...: Custom commands of your own
* Exit: Definitive shutdown of the proxy and its control
*
* All the non-custom commands can be performed in asynchronous or synchronous mode.
*
* Proxy mechanism ensured by pluggable pumps
*
* with built-in low-level {@link org.zeromq.ZProxy.ZmqPump} (zmq.ZMQ): useful for performances
* with built-in high-level {@link org.zeromq.ZProxy.ZPump} (ZeroMQ): useful for {@link org.zeromq.ZProxy.ZPump.Transformer message transformation}, lower performances
* with your own-custom proxy pump implementing a {@link Pump 1-method interface}
*
*
*
* You can have all the above non-customizable features in about these lines of code:
*
* {@code
final ZProxy.Proxy provider = new ZProxy.SimpleProxy()
{
public Socket create(ZContext ctx, ZProxy.Plug place, Object ... args)
{
assert ("TEST".equals(args[0]);
Socket socket = null;
if (place == ZProxy.Plug.FRONT) {
socket = ctx.createSocket(ZMQ.ROUTER);
}
if (place == ZProxy.Plug.BACK) {
socket = ctx.createSocket(ZMQ.DEALER);
}
return socket;
}
public void configure(Socket socket, ZProxy.Plug place, Object ... args)
{
assert ("TEST".equals(args[0]);
int port = -1;
if (place == ZProxy.Plug.FRONT) {
port = socket.bind("tcp://127.0.0.1:6660");
}
if (place == ZProxy.Plug.BACK) {
port = socket.bind("tcp://127.0.0.1:6661");
}
if (place == ZProxy.Plug.CAPTURE && socket != null) {
socket.bind("tcp://127.0.0.1:4263");
}
}
};
ZProxy proxy = ZProxy.newProxy("ProxyOne", provider, "ABRACADABRA", Arrays.asList("TEST"));
}
*
* Once created, the proxy is not started. You have to perform first a start command on it.
* This choice was made because it is easier for a user to start it with one line of code than for the code to internally handle
* different possible starting states (after all, someone may want the proxy started but paused at first or configured in a specific way?)
* and because the a/sync stuff was funnier. Life is unfair ...
* Or maybe an idea is floating in the air?
*
* You can then use it like this:
*
* {@code
final boolean async = false, sync = true;
String status = null;
status = proxy.status();
status = proxy.pause(sync);
status = proxy.start(async);
status = proxy.restart(new ZMsg());
status = proxy.status(async);
status = proxy.stop(sync);
boolean here = proxy.sign();
ZMsg cfg = new ZMsg();
msg.add("CONFIG-1");
ZMsg rcvd = proxy.configure(cfg);
proxy.exit();
status = proxy.status(sync);
assert (!proxy.started());
}
*
*
* A {@link #command(Command, boolean) programmatic interface} with enums is also available.
*
*
*/
// Proxy for 0MQ.
public class ZProxy
{
/**
* Possible places for sockets in the proxy.
*/
public enum Plug
{
FRONT, // The position of the frontend socket.
BACK, // The position of the backend socket.
CAPTURE // The position of the capture socket.
}
// Contract for socket creation and customizable configuration in proxy threading.
public interface Proxy
{
/**
* Creates and initializes (bind, options ...) the socket for the given plug in the proxy.
* The proxy will close them afterwards, and the context as well if not provided in the constructor.
* There is no need to keep a reference on the created socket or the context given in parameter.
*
* @param ctx the context used for initialization.
* @param place the position for the future created socket in the proxy.
* @param args the optional array of arguments that has been passed at the creation of the ZProxy.
* @return the created socket. Possibly null only for capture.
*/
Socket create(ZContext ctx, Plug place, Object... args);
/**
* Configures the given socket.
*
* @param socket the socket to configure
* @param place the position for the socket in the proxy
* @param args the optional array of arguments that has been passed at the creation of the ZProxy.
* @return true if successfully configured, otherwise false
*/
boolean configure(Socket socket, Plug place, Object... args) throws IOException;
/**
* Performs a hot restart of the given socket.
* Usually an unbind/bind but you can use whatever method you like.
*
* @param cfg the custom configuration message sent by the control.
* @param socket the socket to hot restart
* @param place the position for the socket in the proxy
* @param args the optional array of arguments that has been passed at the creation of the ZProxy.
* @return true to perform a cold restart instead, false to do nothing. All the results will be collected from calls for all plugs.
* If any of them returns true, the cold restart is performed.
*/
boolean restart(ZMsg cfg, Socket socket, Plug place, Object... args) throws IOException;
/**
* Configures the proxy with a custom message.
*
* Note: you need to send one (1) mandatory custom response message with the pipe before the end of this call.
*
* @param pipe the control pipe
* @param cfg the custom configuration message sent by the control
* @param frontend the frontend socket
* @param backend the backend socket
* @param capture the optional capture socket
* @param args the optional array of arguments that has been passed at the creation of the ZProxy.
* @return true to continue the proxy, false to exit
*/
boolean configure(Socket pipe, ZMsg cfg, Socket frontend, Socket backend, Socket capture, Object... args);
/**
* Handles a custom command not recognized by the proxy.
*
* Note: you need to send the current state at the end of the call.
*
* @param pipe the control pipe
* @param cmd the unrecognized command
* @param frontend the frontend socket
* @param backend the backend socket
* @param capture the optional capture socket
* @param args the optional array of arguments that has been passed at the creation of the ZProxy.
*
* @return true to continue the proxy, false to exit
*/
boolean custom(Socket pipe, String cmd, Socket frontend, Socket backend, Socket capture, Object... args);
// this may be useful
abstract class SimpleProxy implements Proxy
{
@Override
public boolean restart(ZMsg cfg, Socket socket, Plug place, Object... args)
{
return true;
}
@Override
public boolean configure(Socket pipe, ZMsg cfg, Socket frontend, Socket backend, Socket capture,
Object... args)
{
return true;
}
@Override
public boolean custom(Socket pipe, String cmd, Socket frontend, Socket backend, Socket capture,
Object... args)
{
return true;
}
}
}
/**
* Creates a new proxy in a ZeroMQ way.
* This proxy will be less efficient than the
* {@link #newZProxy(ZContext, String, org.zeromq.ZProxy.Proxy, String, Object...) low-level one}.
*
* @param ctx the context used for the proxy.
* Possibly null, in this case a new context will be created and automatically destroyed afterwards.
* @param name the name of the proxy. Possibly null.
* @param selector the creator of the selector used for the internal polling. Not null.
* @param sockets the sockets creator of the proxy. Not null.
* @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism.
* @param args an optional array of arguments that will be passed at the creation.
*
* @return the created proxy.
* @deprecated use {@link #newZProxy(ZContext, String, Proxy, String, Object...)} instead.
*/
@Deprecated
public static ZProxy newZProxy(ZContext ctx, String name, SelectorCreator selector, Proxy sockets,
String motdelafin, Object... args)
{
return newZProxy(ctx, name, sockets, motdelafin, args);
}
/**
* Creates a new proxy in a ZeroMQ way.
* This proxy will be less efficient than the
* {@link #newZProxy(ZContext, String, org.zeromq.ZProxy.Proxy, String, Object...) low-level one}.
*
* @param ctx the context used for the proxy.
* Possibly null, in this case a new context will be created and automatically destroyed afterwards.
* @param name the name of the proxy. Possibly null.
* @param sockets the sockets creator of the proxy. Not null.
* @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism.
* @param args an optional array of arguments that will be passed at the creation.
*
* @return the created proxy.
*/
public static ZProxy newZProxy(ZContext ctx, String name, Proxy sockets, String motdelafin, Object... args)
{
return new ZProxy(ctx, name, sockets, new ZPump(), motdelafin, args);
}
/**
* Creates a new low-level proxy for better performances.
*
* @param ctx the context used for the proxy.
* Possibly null, in this case a new context will be created and automatically destroyed afterwards.
* @param name the name of the proxy. Possibly null.
* @param selector the creator of the selector used for the internal polling. Not null.
* @param sockets the sockets creator of the proxy. Not null.
* @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism.
* @param args an optional array of arguments that will be passed at the creation.
*
* @return the created proxy.
* @deprecated use {@link #newProxy(ZContext, String, Proxy, String, Object...)} instead.
*/
// creates a new low-level proxy
@Deprecated
public static ZProxy newProxy(ZContext ctx, String name, SelectorCreator selector, Proxy sockets, String motdelafin,
Object... args)
{
return newProxy(ctx, name, sockets, motdelafin, args);
}
/**
* Creates a new low-level proxy for better performances.
*
* @param ctx the context used for the proxy.
* Possibly null, in this case a new context will be created and automatically destroyed afterwards.
* @param name the name of the proxy. Possibly null.
* @param sockets the sockets creator of the proxy. Not null.
* @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism.
* @param args an optional array of arguments that will be passed at the creation.
*
* @return the created proxy.
*/
public static ZProxy newProxy(ZContext ctx, String name, Proxy sockets, String motdelafin, Object... args)
{
return new ZProxy(ctx, name, sockets, new ZmqPump(), motdelafin, args);
}
/**
* Starts the proxy.
*
* @param sync true to read the status in synchronous way, false for asynchronous mode
* @return the read status
*/
public String start(boolean sync)
{
return command(START, sync);
}
/**
* Pauses the proxy.
* A paused proxy will cease processing messages, causing
* them to be queued up and potentially hit the high-water mark on the
* frontend or backend socket, causing messages to be dropped, or writing
* applications to block.
*
* @param sync true to read the status in synchronous way, false for asynchronous mode
* @return the read status
*/
public String pause(boolean sync)
{
return command(PAUSE, sync);
}
/**
* Stops the proxy.
*
* @param sync true to read the status in synchronous way, false for asynchronous mode
* @return the read status
*/
public String stop(boolean sync)
{
return command(STOP, sync);
}
/**
* Sends a command message to the proxy actor.
* Can be useful for programmatic interfaces.
* Does not works with commands {@link #CONFIG CONFIG} and {@link #RESTART RESTART}.
*
* @param command the command to execute. Not null.
* @param sync true to read the status in synchronous way, false for asynchronous mode
* @return the read status
*/
public String command(String command, boolean sync)
{
Utils.checkArgument(
!CONFIG.equals(command),
"CONFIG is not useable with that API. Please use configure(ZMsg) method");
Utils.checkArgument(
!RESTART.equals(command),
"RESTART is not useable with that API. Please use restart(ZMsg) method");
if (STATUS.equals(command)) {
return status(sync);
}
if (EXIT.equals(command)) {
return exit();
}
// consume the status in the pipe
String status = recvStatus();
if (agent.send(command)) {
// the pipe is refilled
if (sync) {
status = status(true);
}
}
return status;
}
/**
* Sends a command message to the proxy actor.
* Can be useful for programmatic interfaces.
* Does not works with commands {@link Command#CONFIG CONFIG} and {@link Command#RESTART RESTART}.
*
* @param command the command to execute.
* @param sync true to read the status in synchronous way, false for asynchronous mode
* @return the read state
*/
public State command(Command command, boolean sync)
{
return State.valueOf(command(command.name(), sync));
}
/**
* Sends a command message to the proxy actor.
* Can be useful for programmatic interfaces.
* Works only with commands {@link Command#CONFIG CONFIG} and {@link Command#RESTART RESTART}.
*
* @param command the command to execute.
* @param msg the custom message to transmit.
* @param sync true to read the status in synchronous way, false for asynchronous mode
* @return the response message
*/
public ZMsg command(Command command, ZMsg msg, boolean sync)
{
if (command == Command.CONFIG) {
return configure(msg);
}
if (command == Command.RESTART) {
String status = restart(msg);
msg = new ZMsg();
msg.add(status);
return msg;
}
return null;
}
/**
* Configures the proxy.
* The distant side has to send back one (1) mandatory response message.
*
* @param msg the custom message sent as configuration tip
* @return the mandatory response message of the configuration.
*/
public ZMsg configure(ZMsg msg)
{
msg.addFirst(CONFIG);
if (agent.send(msg)) {
// consume the status in the pipe
recvStatus();
ZMsg reply = agent.recv();
assert (reply != null);
// refill the pipe with status
agent.send(STATUS);
return reply;
}
return null;
}
/**
* Restarts the proxy. Stays alive.
*
* @param hot null to make a cold restart (closing then re-creation of the sockets)
* or a configuration message to perform a configurable hot restart,
*/
public String restart(ZMsg hot)
{
ZMsg msg = new ZMsg();
msg.add(RESTART);
final boolean cold = hot == null;
if (cold) {
msg.add(Boolean.toString(false));
}
else {
msg.add(Boolean.toString(true));
msg.append(hot);
}
String status = EXITED;
if (agent.send(msg)) {
status = status(false);
}
return status;
}
/**
* Stops the proxy and exits.
*
* @param sync forced to true to read the status in synchronous way.
* @return the read status.
* @deprecated The call is synchronous: the sync parameter is ignored,
* as it leads to many mistakes in case of a provided ZContext.
*/
@Deprecated
public String exit(boolean sync)
{
return exit();
}
/**
* Stops the proxy and exits.
* The call is synchronous.
*
* @return the read status.
*
*/
public String exit()
{
agent.send(EXIT);
exit.awaitSilent();
agent.close();
return EXITED;
}
/**
* Inquires for the status of the proxy.
* This call is synchronous.
*/
public String status()
{
return status(true);
}
/**
* Inquires for the status of the proxy.
*
* @param sync true to read the status in synchronous way, false for asynchronous mode.
* If false, you get the last cached status of the proxy
*/
public String status(boolean sync)
{
if (exit.isExited()) {
return EXITED;
}
try {
String status = recvStatus();
if (agent.send(STATUS) && sync) {
// wait for the response to emulate sync
status = recvStatus();
// AND refill a status
if (EXITED.equals(status) || !agent.send(STATUS)) {
return EXITED;
}
}
return status;
}
catch (ZMQException e) {
return EXITED;
}
}
// receives the last known state of the proxy
private String recvStatus()
{
if (!agent.sign()) {
return EXITED;
}
// receive the status response
final ZMsg msg = agent.recv();
if (msg == null) {
return EXITED;
}
String status = msg.popString();
msg.destroy();
return status;
}
/**
* Binary inquiry for the status of the proxy.
*/
public boolean isStarted()
{
return started();
}
/**
* Binary inquiry for the status of the proxy.
*/
public boolean started()
{
String status = status(true);
return STARTED.equals(status);
}
// to handle commands in a more java-centric way
public enum Command
{
START,
PAUSE,
STOP,
RESTART,
EXIT,
STATUS,
CONFIG
}
// commands for the control pipe
private static final String START = Command.START.name();
private static final String PAUSE = Command.PAUSE.name();
private static final String STOP = Command.STOP.name();
private static final String RESTART = Command.RESTART.name();
private static final String EXIT = Command.EXIT.name();
private static final String STATUS = Command.STATUS.name();
private static final String CONFIG = Command.CONFIG.name();
// to handle states in a more java-centric way
public enum State
{
ALIVE,
STARTED,
PAUSED,
STOPPED,
EXITED
}
// state responses from the control pipe
public static final String STARTED = State.STARTED.name();
public static final String PAUSED = State.PAUSED.name();
public static final String STOPPED = State.STOPPED.name();
public static final String EXITED = State.EXITED.name();
// defines the very first time where no command changing the state has been issued
public static final String ALIVE = State.ALIVE.name();
private static final AtomicInteger counter = new AtomicInteger();
// the endpoint to the distant proxy actor
private final ZAgent agent;
// the synchronizer for exiting
private final Exit exit;
/**
* Creates a new unnamed proxy.
*
* @param selector the creator of the selector used for the proxy.
* @param creator the creator of the sockets of the proxy.
* @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism.
* @param args an optional array of arguments that will be passed at the creation.
* @deprecated use {@link #ZProxy(Proxy, String, Object...)} instead.
*/
@Deprecated
public ZProxy(SelectorCreator selector, Proxy creator, String motdelafin, Object... args)
{
this(creator, motdelafin, args);
}
/**
* Creates a new unnamed proxy.
*
* @param creator the creator of the sockets of the proxy.
* @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism.
* @param args an optional array of arguments that will be passed at the creation.
*/
public ZProxy(Proxy creator, String motdelafin, Object... args)
{
this(null, creator, null, motdelafin, args);
}
/**
* Creates a new named proxy.
*
* @param name the name of the proxy (used in threads naming).
* @param selector the creator of the selector used for the proxy.
* @param creator the creator of the sockets of the proxy.
* @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism.
* @param args an optional array of arguments that will be passed at the creation.
* @deprecated use {@link #ZProxy(String, Proxy, String, Object...)} instead.
*/
@Deprecated
public ZProxy(String name, SelectorCreator selector, Proxy creator, String motdelafin, Object... args)
{
this(name, creator, motdelafin, args);
}
/**
* Creates a new named proxy.
*
* @param name the name of the proxy (used in threads naming).
* @param creator the creator of the sockets of the proxy.
* @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism.
* @param args an optional array of arguments that will be passed at the creation.
*/
public ZProxy(String name, Proxy creator, String motdelafin, Object... args)
{
this(name, creator, null, motdelafin, args);
}
/**
* Creates a new named proxy.
*
* @param ctx the main context used.
* If null, a new context will be created and closed at the stop of the operation.
* If not null, it is the responsibility of the call to close it.
* @param name the name of the proxy (used in threads naming).
* @param selector the creator of the selector used for the proxy.
* @param sockets the creator of the sockets of the proxy.
* @param pump the pump used for the proxy
* @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism.
* @param args an optional array of arguments that will be passed at the creation.
* @deprecated use {@link #ZProxy(ZContext, String, Proxy, Pump, String, Object...)} instead.
*/
@Deprecated
public ZProxy(ZContext ctx, String name, SelectorCreator selector, Proxy sockets, Pump pump, String motdelafin,
Object... args)
{
this(ctx, name, sockets, pump, motdelafin, args);
}
/**
* Creates a new named proxy. A new context will be created and closed at the stop of the operation.
*
* @param name the name of the proxy (used in threads naming).
* @param sockets the creator of the sockets of the proxy.
* @param pump the pump used for the proxy
* @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism.
* @param args an optional array of arguments that will be passed at the creation.
*/
public ZProxy(String name, Proxy sockets, Pump pump, String motdelafin, Object... args)
{
this(null, name, sockets, pump, motdelafin, args);
}
/**
* Creates a new named proxy.
*
* @param ctx the main context used.
* If null, a new context will be created and closed at the stop of the operation.
* If not null, it is the responsibility of the call to close it.
* @param name the name of the proxy (used in threads naming).
* @param sockets the creator of the sockets of the proxy.
* @param pump the pump used for the proxy
* @param motdelafin the final word used to mark the end of the proxy. Null to disable this mechanism.
* @param args an optional array of arguments that will be passed at the creation.
*/
public ZProxy(ZContext ctx, String name, Proxy sockets, Pump pump, String motdelafin, Object... args)
{
super();
// arguments parsing
if (pump == null) {
pump = new ZmqPump();
}
int count = 1;
count += args.length;
Object[] vars = new Object[count];
vars[0] = sockets;
Actor shadow = null;
// copy the arguments and retrieve the last optional shadow given in input
for (int index = 0; index < args.length; ++index) {
Object arg = args[index];
if (arg instanceof Actor) {
shadow = (Actor) arg;
}
vars[index + 1] = arg;
}
// handle the actor
int id = counter.incrementAndGet();
Actor actor = new ProxyActor(name, pump, id);
if (shadow != null) {
actor = new ZActor.Duo(actor, shadow);
}
ZActor zactor = new ZActor(ctx, actor, motdelafin, vars);
agent = zactor.agent(); // NB: the zactor is also its own agent
exit = zactor.exit();
}
// defines a pump that will flow messages from one socket to another
public interface Pump
{
/**
* Transfers a message from one source to one destination, with an optional capture.
*
* @param src the plug of the source socket
* @param source the socket where to receive the message from.
* @param capture the optional sockets where to send the message to. Possibly null.
* @param dst the plug of the destination socket
* @param destination the socket where to send the message to.
*
* @return false in case of error or interruption, true if successfully transferred the message
*/
boolean flow(Plug src, Socket source, Socket capture, Plug dst, Socket destination);
}
// acts in background to proxy messages
private static final class ProxyActor extends ZActor.SimpleActor
{
// the states container of the proxy
private static final class State
{
@Override
public String toString()
{
return "State [alive=" + alive + ", started=" + started + ", paused=" + paused + ", restart=" + restart
+ ", hot=" + hot + "]";
}
// are we alive ?
private boolean alive = false;
// are we started ?
private boolean started = false;
// are we paused ?
private boolean paused = false;
// controls creation of a new agent if asked of a cold restart
private boolean restart = false;
// one-shot configuration for hot restart
private ZMsg hot;
}
// the state of the proxy
private final State state = new State();
// used to transfer message from one socket to another
private final Pump transport;
// the nice name of the proxy
private final String name;
// the provider of the sockets
private Proxy provider;
// the sockets creator user arguments
private Object[] args;
private Socket frontend;
private Socket backend;
private Socket capture;
// creates a new Proxy actor.
public ProxyActor(String name, Pump transport, int id)
{
if (name == null) {
// default basic name
this.name = String.format("ZProxy-%sd", id);
}
else {
this.name = name;
}
this.transport = transport;
}
@Override
public String premiere(Socket pipe)
{
ZMsg reply = new ZMsg();
reply.add(ALIVE);
reply.send(pipe);
return name;
}
// creates the sockets before the start of the proxy
@Override
public List createSockets(ZContext ctx, Object... args)
{
provider = (Proxy) args[0];
this.args = new Object[args.length - 1];
System.arraycopy(args, 1, this.args, 0, this.args.length);
frontend = provider.create(ctx, Plug.FRONT, this.args);
capture = provider.create(ctx, Plug.CAPTURE, this.args);
backend = provider.create(ctx, Plug.BACK, this.args);
assert (frontend != null);
assert (backend != null);
return Arrays.asList(frontend, backend);
}
@Override
public void start(Socket pipe, List sockets, ZPoller poller)
{
// init the state machine
state.alive = true;
state.restart = false;
}
// Process a control message
@Override
public boolean backstage(Socket pipe, ZPoller poller, int events)
{
assert (state.hot == null);
String cmd = pipe.recvStr();
// a message has been received from the API
if (START.equals(cmd)) {
if (start(poller)) {
return status().send(pipe);
}
// unable to start the proxy, exit
state.restart = false;
return false;
}
else if (STOP.equals(cmd)) {
stop();
return status().send(pipe);
}
else if (PAUSE.equals(cmd)) {
pause(poller, true);
return status().send(pipe);
}
else if (RESTART.equals(cmd)) {
String val = pipe.recvStr();
boolean hot = Boolean.parseBoolean(val);
return restart(pipe, hot);
}
else if (STATUS.equals(cmd)) {
return status().send(pipe);
}
else if (CONFIG.equals(cmd)) {
ZMsg cfg = ZMsg.recvMsg(pipe);
boolean rc = provider.configure(pipe, cfg, frontend, backend, capture, args);
cfg.destroy();
return rc;
}
else if (EXIT.equals(cmd)) {
// stops the proxy and the agent.
// the status will be sent at the end of the loop
state.restart = false;
}
else {
return provider.custom(pipe, cmd, frontend, backend, capture, args);
}
return false;
}
// returns the status
private ZMsg status()
{
ZMsg reply = new ZMsg();
if (!state.alive) {
reply.add(EXITED);
}
else if (state.paused) {
reply.add(PAUSED);
}
else if (state.started) {
reply.add(STARTED);
}
else {
reply.add(STOPPED);
}
return reply;
}
// starts the proxy sockets
private boolean start(ZPoller poller)
{
boolean success = true;
if (!state.started) {
try {
success = false;
success |= provider.configure(frontend, Plug.FRONT, args);
success |= provider.configure(backend, Plug.BACK, args);
success |= provider.configure(capture, Plug.CAPTURE, args);
state.started = true;
}
catch (RuntimeException | IOException e) {
e.printStackTrace();
// unable to configure proxy, exit
state.restart = false;
state.started = false;
return false;
}
}
pause(poller, false);
return success;
}
// pauses the proxy sockets
private boolean pause(ZPoller poller, boolean pause)
{
state.paused = pause;
if (pause) {
poller.unregister(frontend);
poller.unregister(backend);
// TODO why not a mechanism for eventually flushing the sockets during the pause?
}
else {
poller.register(frontend, ZPoller.POLLIN);
poller.register(backend, ZPoller.POLLIN);
// Now Wait also until there are either requests or replies to process.
}
return true;
}
private boolean stop()
{
// restart the actor in stopped state
state.started = false;
state.paused = false;
// close connections
state.restart = true;
return true;
}
// handles the restart command in both modes
private boolean restart(Socket pipe, boolean hot)
{
state.restart = true;
if (hot) {
assert (provider != null);
state.hot = ZMsg.recvMsg(pipe);
// continue with the same agent
return true;
}
else {
// stop the loop and restart a new agent
// with the same started state
// the next loop will refill the updated status
return false;
}
}
@Override
public long looping(Socket pipe, ZPoller poller)
{
state.hot = null;
return super.looping(pipe, poller);
}
// a message has been received for the proxy to process
@Override
public boolean stage(Socket socket, Socket pipe, ZPoller poller, int events)
{
if (socket == frontend) {
// Process a request.
return transport.flow(Plug.FRONT, frontend, capture, Plug.BACK, backend);
}
if (socket == backend) {
// Process a reply.
return transport.flow(Plug.BACK, backend, capture, Plug.FRONT, frontend);
}
return false;
}
@Override
public boolean looped(Socket pipe, ZPoller poller)
{
if (state.restart) {
if (state.hot == null) {
// caught the cold restart
return false;
}
else {
// caught the hot restart
ZMsg cfg = state.hot;
state.hot = null;
// we perform a cold restart if the provider says so
boolean cold;
ZMsg dup = cfg.duplicate();
try {
cold = provider.restart(dup, frontend, Plug.FRONT, this.args);
dup.destroy();
dup = cfg.duplicate();
cold |= provider.restart(dup, backend, Plug.BACK, this.args);
dup.destroy();
dup = cfg.duplicate();
cold |= provider.restart(dup, capture, Plug.CAPTURE, this.args);
dup.destroy();
cfg.destroy();
}
catch (RuntimeException | IOException e) {
e.printStackTrace();
state.restart = false;
return false;
}
// cold restart means the loop has to stop.
return !cold;
}
}
return true;
}
// called in the proxy thread when it stopped.
@Override
public boolean destroyed(ZContext ctx, Socket pipe, ZPoller poller)
{
if (capture != null) {
capture.close();
}
state.alive = false;
if (!state.restart) {
status().send(pipe);
}
return state.restart;
}
}
/**
* A pump that reads a message as a whole before transmitting it.
* It offers a way to transform messages for capture and destination.
*/
public static class ZPump implements Pump
{
private static final Identity IDENTITY = new Identity();
// the messages transformer
private final Transformer transformer;
// transforms one message into another
public interface Transformer
{
/**
* Transforms a ZMsg into another ZMsg.
* Please note that this will be used during the message transfer,
* so lengthy operations will have a cost on performances by definition.
* If you return back another message than the one given in input, then this one has to be destroyed by you.
* @param msg the message to transform
* @param src the source plug
* @param dst the destination plug
* @return the transformed message
*/
ZMsg transform(ZMsg msg, Plug src, Plug dst);
}
private static class Identity implements Transformer
{
@Override
public ZMsg transform(ZMsg msg, Plug src, Plug dst)
{
return msg;
}
}
public ZPump()
{
this(null);
}
public ZPump(Transformer transformer)
{
super();
this.transformer = transformer == null ? IDENTITY : transformer;
}
@Override
public boolean flow(Plug splug, Socket source, Socket capture, Plug dplug, Socket destination)
{
boolean success;
// we read the whole message
ZMsg msg = ZMsg.recvMsg(source);
if (msg == null) {
return false;
}
if (capture != null) {
// Copy transformed message to capture socket if any message
// TODO what if the transformer modifies or destroys the original message ?
ZMsg cpt = transformer.transform(msg, splug, Plug.CAPTURE);
// boolean destroy = !msg.equals(cpt); // TODO ?? which one
boolean destroy = msg != cpt;
success = cpt.send(capture, destroy);
if (!success) {
// not successful, but we can still try to send it to the destination
}
}
ZMsg dst = transformer.transform(msg, splug, dplug);
// we send the whole transformed message
success = dst.send(destination);
// finished
msg.destroy();
return success;
}
}
/**
* A specialized transport for better transmission purposes
* that will send each packets individually instead of the whole message.
*/
private static final class ZmqPump implements Pump
{
// transfers each message as a whole by sending each packet received to the capture socket
@Override
public boolean flow(Plug splug, Socket source, Socket capture, Plug dplug, Socket destination)
{
boolean rc;
SocketBase src = source.base();
SocketBase dst = destination.base();
SocketBase cpt = capture == null ? null : capture.base();
// we transfer the whole message
while (true) {
// we read the packet
Msg msg = src.recv(0);
if (msg == null) {
return false;
}
long more = src.getSocketOpt(zmq.ZMQ.ZMQ_RCVMORE);
if (more < 0) {
return false;
}
// Copy message to capture socket if any packet
if (cpt != null) {
Msg ctrl = new Msg(msg);
rc = cpt.send(ctrl, more > 0 ? zmq.ZMQ.ZMQ_SNDMORE : 0);
if (!rc) {
// not successful, but we can still try to send it to the destination
}
}
// we send the packet
rc = dst.send(msg, more > 0 ? zmq.ZMQ.ZMQ_SNDMORE : 0);
if (!rc) {
return false;
}
if (more == 0) {
break;
}
}
return true;
}
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZSocket.java 0000664 0000000 0000000 00000017354 14557711263 0022024 0 ustar 00root root 0000000 0000000 package org.zeromq;
import java.util.concurrent.atomic.AtomicBoolean;
import zmq.Msg;
import zmq.SocketBase;
import zmq.ZError;
import zmq.ZMQ;
/**
* ZeroMQ sockets present an abstraction of an asynchronous message queue, with the exact queuing
* semantics depending on the socket type in use. Where conventional sockets transfer streams of
* bytes or discrete datagrams, ZeroMQ sockets transfer discrete messages.
*
* ZeroMQ sockets being asynchronous means that the timings of the physical connection setup and
* tear down, reconnect and effective delivery are transparent to the user and organized by ZeroMQ
* itself. Further, messages may be queued in the event that a peer is unavailable to receive them.
*
*/
public class ZSocket implements AutoCloseable
{
private final SocketBase socketBase;
private final AtomicBoolean isClosed = new AtomicBoolean(false);
/**
* Create a ZeroMQ socket
*
* @param socketType ZeroMQ socket type
*/
public ZSocket(final int socketType)
{
socketBase = ManagedContext.getInstance().createSocket(socketType);
}
/**
* Create a ZeroMQ socket
*
* @param socketType ZeroMQ Socket type
*/
public ZSocket(final SocketType socketType)
{
this(socketType.type());
}
/**
* Retrieve the socket type for the current 'socket'. The socket type is specified at socket
* creation time and cannot be modified afterwards.
*
* @return the socket's type.
*/
public SocketType getSocketType()
{
return SocketType.type(getType());
}
/**
* Retrieve the socket type for the current 'socket'. The socket type is specified at socket
* creation time and cannot be modified afterwards.
*
* @see ZSocket#getSocketType()
* @return the socket's type.
*/
public int getType()
{
return (int) getOption(ZMQ.ZMQ_TYPE);
}
/**
* Creates an endpoint for accepting connections and binds to it.
*
* The endpoint argument is a string consisting of two parts as follows: transport ://address. The
* transport part specifies the underlying transport protocol to use. The meaning of the address
* part is specific to the underlying transport protocol selected.
*
*
* @param endpoint the endpoint to bind to
* @return returns true if bind to the endpoint was successful
*/
public boolean bind(final String endpoint)
{
final boolean result = socketBase.bind(endpoint);
mayRaise();
return result;
}
/**
* Stop accepting connections on a socket.
*
* Shall unbind from the endpoint specified by the endpoint argument.
*
*
* @param endpoint the endpoint to unbind from
* @return returns true if unbind to the endpoint was successful
*/
public boolean unbind(final String endpoint)
{
final boolean result = socketBase.bind(endpoint);
mayRaise();
return result;
}
/**
* Connects the socket to an endpoint and then accepts incoming connections on that endpoint.
*
* The endpoint is a string consisting of a transport :// followed by an address. The transport
* specifies the underlying protocol to use. The address specifies the transport-specific address
* to connect to.
*
*
* @param endpoint the endpoint to connect to
* @return returns true if connecting to the endpoint was successful
*/
public boolean connect(final String endpoint)
{
final boolean result = socketBase.connect(endpoint);
mayRaise();
return result;
}
/**
* Disconnecting a socket from an endpoint.
*
* @param endpoint the endpoint to disconnect from
* @return returns true if disconnecting to endpoint was successful
*/
public boolean disconnect(final String endpoint)
{
final boolean result = socketBase.termEndpoint(endpoint);
mayRaise();
return result;
}
/**
* Returns a boolean value indicating if the multipart message currently being read from the
* {@code Socket} and has more message parts to follow. If there are no message parts to follow or
* if the message currently being read is not a multipart message a value of false shall be
* returned. Otherwise, a value of true shall be returned.
*
* @return true if there are more messages to receive.
*/
public final boolean hasReceiveMore()
{
return (int) getOption(ZMQ.ZMQ_RCVMORE) == 1;
}
public void subscribe(byte[] topic)
{
setOption(ZMQ.ZMQ_SUBSCRIBE, topic);
}
public void subscribe(String topic)
{
setOption(ZMQ.ZMQ_SUBSCRIBE, topic.getBytes(ZMQ.CHARSET));
}
public void unsubscribe(byte[] topic)
{
setOption(ZMQ.ZMQ_UNSUBSCRIBE, topic);
}
public void unsubscribe(String topic)
{
setOption(ZMQ.ZMQ_UNSUBSCRIBE, topic.getBytes(ZMQ.CHARSET));
}
public int send(byte[] b)
{
return send(b, 0);
}
public int send(byte[] b, int flags)
{
final Msg msg = new Msg(b);
if (socketBase.send(msg, flags)) {
return msg.size();
}
mayRaise();
return -1;
}
public int sendMessage(Msg msg, int flags)
{
if (socketBase.send(msg, flags)) {
return msg.size();
}
mayRaise();
return -1;
}
public int sendMessage(Msg msg)
{
return sendMessage(msg, 0);
}
/**
* Send a frame
*
* @param frame
* @param flags
* @return return true if successful
*/
public boolean sendFrame(ZFrame frame, int flags)
{
final byte[] data = frame.getData();
final Msg msg = new Msg(data);
if (socketBase.send(msg, flags)) {
return true;
}
mayRaise();
return false;
}
public boolean sendMessage(ZMsg message)
{
ZFrame frame = message.pop();
boolean rc = false;
while (frame != null) {
rc = sendFrame(frame, !message.isEmpty() ? ZMQ.ZMQ_MORE : 0);
if (!rc) {
break;
}
frame = message.pop();
}
return rc;
}
public int sendStringUtf8(String str)
{
return sendStringUtf8(str, 0);
}
public int sendStringUtf8(String str, int flags)
{
final byte[] b = str.getBytes(ZMQ.CHARSET);
return send(b, flags);
}
public byte[] receive()
{
return receive(0);
}
public byte[] receive(int flags)
{
final Msg msg = socketBase.recv(flags);
if (msg == null) {
return null;
}
return msg.data();
}
public Msg receiveMessage()
{
return socketBase.recv(0);
}
public Msg receiveMessage(int flags)
{
return socketBase.recv(flags);
}
public String receiveStringUtf8()
{
return receiveStringUtf8(0);
}
public String receiveStringUtf8(int flags)
{
final byte[] b = receive(flags);
return new String(b, ZMQ.CHARSET);
}
private void mayRaise()
{
final int errno = socketBase.errno();
if (errno != 0 && errno != ZError.EAGAIN) {
throw new ZMQException(errno);
}
}
private void setOption(int option, Object value)
{
socketBase.setSocketOpt(option, value);
}
private Object getOption(int option)
{
return socketBase.getSocketOptx(option);
}
/**
* {@inheritDoc}
*/
@Override
public void close()
{
if (isClosed.compareAndSet(false, true)) {
ManagedContext.getInstance().destroy(socketBase);
}
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZStar.java 0000664 0000000 0000000 00000063471 14557711263 0021506 0 ustar 00root root 0000000 0000000 package org.zeromq;
import java.nio.channels.Selector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.zeromq.ZMQ.Socket;
import org.zeromq.ZThread.IAttachedRunnable;
import zmq.util.Objects;
import zmq.util.function.BiFunction;
/**
* First implementation for the base of a remotely controlled background service for 0MQ.
*
* A side or endpoint designates the same thing: the thread where lives one of the two parts of the system.
* A {@link ZStar Star} has 2 sides (with a trial to use theater terms for fun (: and hopefully clarity :)
*
* the Corbeille side, or control side
*
* This is where one can {@link ZAgent#send(ZMsg) send} and {@link ZAgent#recv() receive} control messages to the underneath star via the ZStar agent.
*
* The ZStar lives on this side and is a way to safely communicate with the distant provided star.
*
* Note: Corbeille is a french word designing the most privileged places in the theater where the King was standing,
* 1st floor, just above the orchestra (Wikipedia...).
* the Plateau side, or background side where all the performances are made by the provided star.
*
* The provided star is living on the Plateau.
*
* The Plateau is made of the Stage where effective acting takes place and of the Wings
* where the provided star can perform control decisions to stop or continue the acting.
*
* From this side, the work is done via callbacks using the template-method pattern, applied with interfaces (?)
*
* Communication between the 2 sides is ensured by {@link #pipe() pipes} that SHALL NEVER be mixed between background and control sides.
* Instead, each side use its own pipe to communicate with the other one, transmitting only ØMQ messages.
* The main purpose of this class is to ease this separation and to provide contracts, responsibilities and action levers for each side.
*
* For example, instead of using pipe() which is useful to have more control, fast users would want to call {@link #agent()} to get the agent
* or directly call {@link #send(ZMsg)} or {@link #recv()} from the ZStar as the ZStar is itself an agent!
*
* The ZStar takes care of the establishment of the background processing, calling the provided star
* at appropriate times. It can also manage the {@link ZAgent#sign() exited} state on the control side,
* if providing a non-null "Mot de la Fin".
*
* It also takes care of the closing of sockets and context if it had to create one.
*
* A {@link Star star} is basically a contract interface that anyone who uses this ZStar to create a background service SHALL comply to.
* PS: Je sais qu'il y a une différence entre acteur et comédien :)
* PPS: I know nothing about theater!
*
* For an example of code, please refer to the {@link ZActor} source code.
*
*/
// remote controlled background message processing API for 0MQ.
public class ZStar implements ZAgent
{
/**
* Contract interface when acting in plain sight.
*/
// contract interface for acting with the spot lights on
public interface Star
{
/**
* Called when the star is in the wings.
* Hint: Can be used to initialize the service, or ...
* Key point: no loop has started already.
*/
void prepare();
/**
* Called when the star in on stage, just before acting.
* Hint: Can be used to poll events or get input/events from other sources, or ...
* Key point: a loop just started.
*
* @return the number of events to process
*/
int breathe();
/**
* Where acting takes place ...
* Hint: Can be used to process the events or input acquired from the previous step, or ...
* Key point: in the middle of a loop.
* Decision: to act on the next loop or not
*
* @param events the number of events to process
* @return true to continue till the end of the act, false to stop loopS here.
*/
boolean act(int events);
/**
* Called as an interval between each act.
* Hint: Can be used to perform decisions to continue next loop or not, or to send computed data to outputs, or ...
* Key point: at the end of a loop.
* Decision: to act on the next loop or not
*
* @return true to continue acting, false to stop loopS here.
*/
boolean entract();
/**
* Does the star want to renew for a new performance ?
* Hint: Can be used to perform decisions to continue looping or not, or to send computed data to outputs, or ...
* Key point: the inner looping mechanism just ended
* Decision: to exit or not
*
* @return true to restart acting, false to leave here
*/
boolean renews();
}
/**
* Utility class with callback for when the Star has finished its performances.
*/
public interface TimeTaker
{
/**
* Called when the show is finished but no cleaning is still done.
* Useful to make the background thread wait a little bit, for example.
*
* @param ctx the shadow context
*/
void party(ZContext ctx);
}
// party time easily done. Wait for the specified amount of time
public static void party(long time, TimeUnit unit)
{
ZMQ.sleep(time, unit);
}
// contract for a creator of stars
public interface Fortune extends TimeTaker
{
/**
* This is the grand premiere!
* Called when the star enters the plateau.
* The show is about to begin. Inform the public about that.
* Called before the creation of the first star and its sockets
*
* @param mic the pipe to the Corbeille side
* @param args the arguments passed as parameters of the star constructor
*
* @return the name of the upcoming performance.
*/
String premiere(Socket mic, Object... args);
/**
* Creates a star.
*
* @param ctx the context used for the creation of the sockets
* @param mic the pipe to the Corbeille side
* @param sel the selector used for polling
* @param count the number of times a star was created.
* @param previous the previous star if any (null at the first creation)
* @param args the arguments passed as parameters of the star constructor
*
* @return a new star is born!
*
* @deprecated use {@link #create(ZContext, Socket, int, Star, Object...)} instead.
*/
@Deprecated
default Star create(ZContext ctx, Socket mic, Selector sel, int count, Star previous, Object... args)
{
return this.create(ctx, mic, count, previous, args);
}
/**
* Creates a star.
*
* @param ctx the context used for the creation of the sockets
* @param mic the pipe to the Corbeille side
* @param count the number of times a star was created.
* @param previous the previous star if any (null at the first creation)
* @param args the arguments passed as parameters of the star constructor
*
* @return a new star is born!
*/
Star create(ZContext ctx, Socket mic, int count, Star previous, Object... args);
/**
* The show is over.
* Called when the show is over.
*
* @param mic the pipe to the Corbeille side
* @return true to allow to spread the word and close all future communications
*/
boolean interview(Socket mic);
}
/**
* Control for the end of the remote operations.
*/
public interface Exit
{
/**
* Causes the current thread to wait in blocking mode until the end of the remote operations.
*/
void awaitSilent();
/**
* Causes the current thread to wait in blocking mode until the end of the remote operations,
* unless the thread is interrupted.
*
* If the current thread:
*
* has its interrupted status set on entry to this method; or
* is {@linkplain Thread#interrupt interrupted} while waiting,
*
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* @throws InterruptedException if the current thread is interrupted
* while waiting
*/
void await() throws InterruptedException;
/**
* Causes the current thread to wait in blocking mode until the end of the remote operations,
* unless the thread is interrupted, or the specified waiting time elapses.
*
* If the current thread:
*
* has its interrupted status set on entry to this method; or
* is {@linkplain Thread#interrupt interrupted} while waiting,
*
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* If the specified waiting time elapses then the value {@code false}
* is returned. If the time is less than or equal to zero, the method
* will not wait at all.
*
* @param timeout the maximum time to wait
* @param unit the time unit of the {@code timeout} argument
* @return {@code true} if the remote operations ended and {@code false}
* if the waiting time elapsed before the remote operations ended
* @throws InterruptedException if the current thread is interrupted
* while waiting
*/
boolean await(long timeout, TimeUnit unit) throws InterruptedException;
/**
* Checks in non-blocking mode, if the remote operations have ended.
* @return true if the runnable where the remote operations occurred if finished, otherwise false.
*/
boolean isExited();
}
/**
* Returns the Corbeille endpoint.
* Can be used to send or receive control messages to the distant star via Backstage.
*
* @return the agent/mailbox used to send and receive control messages to and from the star.
*/
public ZAgent agent()
{
return agent;
}
/**
* Returns the control of the proper exit of the remote operations.
* @return a structure used for checking the end of the remote operations.
*/
public Exit exit()
{
return plateau;
}
/**
* Creates a new ZStar.
*
* @param selector the creator of the selector used on the Plateau.
* @param fortune the creator of stars on the Plateau
* @param motdelafin the final word used to mark the end of the star. Null to disable this mechanism.
* @param bags the optional arguments that will be passed to the distant star
* @deprecated use {@link ZStar#ZStar(Fortune, String, Object...)} instead.
*/
@Deprecated
public ZStar(SelectorCreator selector, Fortune fortune, String motdelafin, Object... bags)
{
this(fortune, motdelafin, bags);
}
/**
* Creates a new ZStar.
*
* @param fortune the creator of stars on the Plateau
* @param motdelafin the final word used to mark the end of the star. Null to disable this mechanism.
* @param bags the optional arguments that will be passed to the distant star
*/
public ZStar(final Fortune fortune, String motdelafin, final Object... bags)
{
this((ZContext) null, fortune, motdelafin, bags);
}
/**
* Creates a new ZStar.
*
* @param context
* the main context used. If null, a new context will be created
* and closed at the stop of the operation.
* If not null, it is the responsibility of the caller to close it.
*
* @param selector the creator of the selector used on the Plateau.
* @param fortune the creator of stars on the Plateau
* @param motdelafin the final word used to mark the end of the star. Null to disable this mechanism.
* @param bags the optional arguments that will be passed to the distant star
* @deprecated use {@link ZStar#ZStar(ZContext, Fortune, String, Object...)} instead.
*/
@Deprecated
public ZStar(final ZContext context, final SelectorCreator selector, final Fortune fortune, String motdelafin,
final Object... bags)
{
this(context, fortune, ZAgent.Creator::create, motdelafin, bags);
}
/**
* Creates a new ZStar.
*
* @param context
* the main context used. If null, a new context will be created
* and closed at the stop of the operation.
* If not null, it is the responsibility of the caller to close it.
*
* @param fortune the creator of stars on the Plateau
* @param motdelafin the final word used to mark the end of the star. Null to disable this mechanism.
* @param bags the optional arguments that will be passed to the distant star
*/
public ZStar(final ZContext context, final Fortune fortune, String motdelafin, final Object... bags)
{
this(context, fortune, ZAgent.Creator::create, motdelafin, bags);
}
/**
* Creates a new ZStar.
*
* @param context
* the main context used. If null, a new context will be created
* and closed at the stop of the operation.
* If not null, it is the responsibility of the caller to close it.
*
* @param selector the creator of the selector used on the Plateau.
* @param fortune the creator of stars on the Plateau. Not null.
* @param agent the creator of the agent. Not null.
* @param motdelafin the final word used to mark the end of the star. Null to disable this mechanism.
* @param bags the optional arguments that will be passed to the distant star
* @deprecated use {@link ZStar#ZStar(ZContext, Fortune, BiFunction, String, Object...)} instead.
*/
@Deprecated
public ZStar(final ZContext context, final SelectorCreator selector, final Fortune fortune,
BiFunction agent, String motdelafin, final Object... bags)
{
this(context, fortune, agent, motdelafin, bags);
}
/**
* Creates a new ZStar.
*
* @param context
* the main context used. If null, a new context will be created
* and closed at the stop of the operation.
* If not null, it is the responsibility of the caller to close it.
*
* @param fortune the creator of stars on the Plateau. Not null.
* @param agent the creator of the agent. Not null.
* @param motdelafin the final word used to mark the end of the star. Null to disable this mechanism.
* @param bags the optional arguments that will be passed to the distant star
*/
public ZStar(final ZContext context, final Fortune fortune, BiFunction agent,
String motdelafin, final Object... bags)
{
super();
Objects.requireNonNull(agent, "Agent creator has to be supplied");
Objects.requireNonNull(fortune, "Fortune has to be supplied");
// entering platform to load trucks
// initialize the context
ZContext chef = context;
ZContext producer = null;
if (chef == null) {
// no context provided, create one
chef = new ZContext();
// it will be destroyed, so this is the main context
producer = chef;
}
// context of the star. never null
ZContext context1 = chef;
assert (context1 != null);
// retrieve the last optional set and entourage given in input
Set set = null;
Entourage entourage = null;
for (Object bag : bags) {
if (bag instanceof Set) {
set = (Set) bag;
}
if (bag instanceof Entourage) {
entourage = (Entourage) bag;
}
}
if (set == null) {
set = new SimpleSet();
}
final List train = new ArrayList<>(6 + bags.length);
train.add(set);
train.add(fortune);
train.add(producer);
train.add(entourage);
train.add(motdelafin);
// 5 mandatory wagons
train.addAll(Arrays.asList(bags));
// now going to the plateau
Socket phone = ZThread.fork(chef, plateau, train.toArray());
this.agent = agent.apply(phone, motdelafin);
}
// communicating agent with the star for the Corbeille side
private final ZAgent agent;
// distant runnable where acting takes place
private final Plateau plateau = new Plateau();
/**
* Creates a new agent for the star.
*
* @param phone the socket used to communicate with the star
* @param secret the specific keyword indicating the death of the star and locking the agent. Null to override the lock mechanism.
* @return the newly created agent for the star.
*/
@Deprecated
protected ZAgent agent(Socket phone, String secret)
{
return ZAgent.Creator.create(phone, secret);
}
// the plateau where the acting will take place (stage and backstage), or
// the forked runnable containing the loop processing all messages in the background
private static final class Plateau implements IAttachedRunnable, Exit
{
private static final AtomicInteger shows = new AtomicInteger();
// id if unnamed
private final int number = shows.incrementAndGet();
// waiting-flag for the end of the remote operations
private final CountDownLatch exit = new CountDownLatch(1);
@Override
public void run(final Object[] train, final ZContext chef, final Socket mic)
{
final int mandat = 5;
// end of a trip can be a bit messy...
Fortune star = (Fortune) train[1];
final Entourage entourage = (Entourage) train[3];
final ZContext producer = (ZContext) train[2];
final Set set = (Set) train[0];
// the word informing the world that the plateau is closed and the star vanished
final String gossip = (String) train[4];
// prune our mandatory transit variables from the arguments
final Object[] bags = new Object[train.length - mandat];
System.arraycopy(train, mandat, bags, 0, bags.length);
// leaving unloaded platform
if (entourage != null) {
entourage.breakaleg(chef, star, mic, bags);
}
// now entering artistic zone
try {
// Premiere !
String name = star.premiere(mic, bags);
// put the name of the performance on the front door with lightnings
set.lights(name, number);
// star is entering the wings
showMustGoOn(chef, set, mic, star, bags);
// star is leaving the plateau
}
finally {
try {
// star is interviewed about this event
boolean tell = star.interview(mic);
if (tell && gossip != null) {
// inform the Corbeille side of the future closing of the plateau and the vanishing of the star
mic.send(gossip);
}
// we are not in a hurry at this point when cleaning up the remains of a good show ...
star.party(chef);
if (entourage != null) {
entourage.party(chef);
}
// Sober again ...
// show is over, time to close
chef.close();
if (producer != null) {
// this is a self-generated context, destroy it
producer.close();
}
}
finally {
exit.countDown();
}
}
}
/******************************************************************************/
/* TAP TAP TAP | | TAP | | TAP | | TAP | | | | | | | | | | | | | | | | | | | |*/
/******************************************************************************/
// starts the performance
private void showMustGoOn(final ZContext chef, final Set set, final Socket phone, final Fortune fortune,
final Object... bags)
{
int shows = 0;
/** on the spot lights, the star in only an actor **/
Star actor = null;
/** double-while-loop enables the restarting of a new star for the same acting on the same stage **/
/// so existing sockets can be closed and recreated in order to perform a cold restart or a stop **/
do {
actor = fortune.create(chef, phone, shows++, actor, bags);
/** a new star is born! And an acting one! **/
actor.prepare();
/** actor is leaving the wings **/
while (!set.fire()) {
/** actor decides when the act will begin **/
int events = actor.breathe();
/** perform a new act of the show **/
if (!actor.act(events)) {
// Context has been shut down
break;
}
/** end of the act, back to the wings **/
if (!actor.entract()) {
// star has decided to stop acting
break;
}
}
} while (actor.renews());
// star is leaving the Plateau and the show
}
/******************************************************************************/
/* | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |*/
/******************************************************************************/
// NB: never use green color on the stage floor of a french theater. Or something bad will happen...
@Override
public void awaitSilent()
{
try {
exit.await();
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
@Override
public void await() throws InterruptedException
{
exit.await();
}
@Override
public boolean await(long timeout, TimeUnit unit) throws InterruptedException
{
return exit.await(timeout, unit);
}
@Override
public boolean isExited()
{
return exit.getCount() == 0;
}
}
@Override
public ZMsg recv()
{
return agent.recv();
}
@Override
public ZMsg recv(int timeout)
{
return agent.recv(timeout);
}
@Override
public ZMsg recv(boolean wait)
{
return agent.recv(wait);
}
@Override
public boolean send(ZMsg message)
{
return agent.send(message);
}
@Override
public boolean send(ZMsg msg, boolean destroy)
{
return agent.send(msg, destroy);
}
@Override
public boolean send(String word)
{
return agent.send(word);
}
@Override
public boolean send(String word, boolean more)
{
return agent.send(word, more);
}
@Override
public Socket pipe()
{
return agent.pipe();
}
@Override
public boolean sign()
{
return agent.sign();
}
@Override
public void close()
{
agent.close();
}
public interface Set
{
/**
* Puts the performance name on the front door with big lights.
* @param name the name of the performance.
* @param id the performance number.
*/
void lights(String name, int id);
/**
* Is the set on fire ?
* @return true if it is time to leave the place.
*/
boolean fire();
}
public static class SimpleSet implements Set
{
@Override
public boolean fire()
{
return Thread.currentThread().isInterrupted();
}
@Override
public void lights(String name, int id)
{
if (name == null) {
name = createDefaultName("Star-%d", id);
}
Thread.currentThread().setName(name);
}
public static String createDefaultName(final String format, final int id)
{
return String.format(format, id);
}
}
/**
* Utility class with calls surrounding the execution of the Star.
*/
public interface Entourage extends TimeTaker
{
/**
* Called when the show is about to start.
* Can be a useful point in the whole process from time to time.
*
* @param ctx the context provided in the creation step
* @param fortune the creator of stars
* @param phone the socket used to communicate with the Agent
* @param bags the optional arguments that were passed at the creation
*/
void breakaleg(ZContext ctx, Fortune fortune, Socket phone, Object... bags);
// well ...
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZThread.java 0000664 0000000 0000000 00000006350 14557711263 0021775 0 ustar 00root root 0000000 0000000 package org.zeromq;
import org.zeromq.ZMQ.Error;
import org.zeromq.ZMQ.Socket;
import java.util.Locale;
public class ZThread
{
private ZThread()
{
}
public interface IAttachedRunnable
{
void run(Object[] args, ZContext ctx, Socket pipe);
}
public interface IDetachedRunnable
{
void run(Object[] args);
}
private static class ShimThread extends Thread
{
private ZContext ctx;
private IAttachedRunnable attachedRunnable;
private IDetachedRunnable detachedRunnable;
private final Object[] args;
private Socket pipe;
protected ShimThread(ZContext ctx, IAttachedRunnable runnable, Object[] args, Socket pipe)
{
assert (ctx != null);
assert (pipe != null);
assert (runnable != null);
this.ctx = ctx;
this.attachedRunnable = runnable;
this.args = args;
this.pipe = pipe;
this.setUncaughtExceptionHandler(ctx.getUncaughtExceptionHandler());
}
public ShimThread(IDetachedRunnable runnable, Object[] args)
{
assert (runnable != null);
this.detachedRunnable = runnable;
this.args = args;
}
@Override
public void run()
{
if (attachedRunnable != null) {
try {
attachedRunnable.run(args, ctx, pipe);
}
catch (ZMQException e) {
if (e.getErrorCode() != Error.ETERM.getCode()) {
throw e;
}
}
ctx.destroy();
}
else {
detachedRunnable.run(args);
}
}
}
// --------------------------------------------------------------------------
// Create a detached thread. A detached thread operates autonomously
// and is used to simulate a separate process. It gets no ctx, and no
// pipe.
public static void start(IDetachedRunnable runnable, Object... args)
{
// Prepare child thread
Thread shim = new ShimThread(runnable, args);
shim.setDaemon(true);
shim.start();
}
// --------------------------------------------------------------------------
// Create an attached thread. An attached thread gets a ctx and a PAIR
// pipe back to its parent. It must monitor its pipe, and exit if the
// pipe becomes unreadable. Returns pipe, or null if there was an error.
public static Socket fork(ZContext ctx, IAttachedRunnable runnable, Object... args)
{
Socket pipe = ctx.createSocket(SocketType.PAIR);
assert (pipe != null);
pipe.bind(String.format(Locale.ENGLISH, "inproc://zctx-pipe-%d", pipe.hashCode()));
// Connect child pipe to our pipe
ZContext ccontext = ctx.shadow();
Socket cpipe = ccontext.createSocket(SocketType.PAIR);
assert (cpipe != null);
cpipe.connect(String.format(Locale.ENGLISH, "inproc://zctx-pipe-%d", pipe.hashCode()));
// Prepare child thread
Thread shim = new ShimThread(ccontext, runnable, args, cpipe);
shim.start();
return pipe;
}
}
jeromq-0.6.0/src/main/java/org/zeromq/ZTimer.java 0000664 0000000 0000000 00000006750 14557711263 0021652 0 ustar 00root root 0000000 0000000 package org.zeromq;
import zmq.util.Draft;
/**
* Manages set of timers.
*
* Timers can be added with a given interval, when the interval of time expires after addition, handler method is executed with given arguments.
* Timer is repetitive and will be executed over time until canceled.
*
* This is a DRAFT class, and may change without notice.
* @deprecated scheduled for removal in future release. Please use {@link org.zeromq.timer.ZTimer} instead
*/
@Draft
@Deprecated
public final class ZTimer
{
/**
* Opaque representation of a timer.
* @deprecated use {@link org.zeromq.timer.ZTimer.Timer} instead
*/
@Deprecated
public static final class Timer
{
private final org.zeromq.timer.ZTimer.Timer delegate;
Timer(org.zeromq.timer.ZTimer.Timer delegate)
{
this.delegate = delegate;
}
}
/**
* @deprecated use {@link org.zeromq.timer.TimerHandler} instead
*/
@Deprecated
public interface Handler extends org.zeromq.timer.TimerHandler
{
}
private final org.zeromq.timer.ZTimer timer = new org.zeromq.timer.ZTimer();
/**
* Add timer to the set, timer repeats forever, or until cancel is called.
* @param interval the interval of repetition in milliseconds.
* @param handler the callback called at the expiration of the timer.
* @param args the optional arguments for the handler.
* @return an opaque handle for further cancel.
*/
public Timer add(long interval, Handler handler, Object... args)
{
if (handler == null) {
return null;
}
return new Timer(timer.add(interval, handler, args));
}
/**
* Changes the interval of the timer.
*
* This method is slow, canceling existing and adding a new timer yield better performance.
* @param timer the timer to change the interval to.
* @return true if set, otherwise false.
* @deprecated use {@link org.zeromq.timer.ZTimer.Timer#setInterval(long)} instead
*/
@Deprecated
public boolean setInterval(Timer timer, long interval)
{
return timer.delegate.setInterval(interval);
}
/**
* Reset the timer.
*
* This method is slow, canceling existing and adding a new timer yield better performance.
* @param timer the timer to reset.
* @return true if reset, otherwise false.
* @deprecated use {@link org.zeromq.timer.ZTimer.Timer#reset()} instead
*/
@Deprecated
public boolean reset(Timer timer)
{
return timer.delegate.reset();
}
/**
* Cancel a timer.
*
* @param timer the timer to cancel.
* @return true if cancelled, otherwise false.
* @deprecated use {@link org.zeromq.timer.ZTimer.Timer#cancel()} instead
*/
@Deprecated
public boolean cancel(Timer timer)
{
return timer.delegate.cancel();
}
/**
* Returns the time in millisecond until the next timer.
*
* @return the time in millisecond until the next timer.
*/
public long timeout()
{
return timer.timeout();
}
/**
* Execute the timers.
*
* @return the number of timers triggered.
*/
public int execute()
{
return timer.execute();
}
/**
* Sleeps until at least one timer can be executed and execute the timers.
*
* @return the number of timers triggered.
*/
public int sleepAndExecute()
{
return timer.sleepAndExecute();
}
}
jeromq-0.6.0/src/main/java/org/zeromq/package-info.java 0000664 0000000 0000000 00000000665 14557711263 0022763 0 ustar 00root root 0000000 0000000 /**
* Provides high-level bindings for ØMQ.
*
* This is the java equivalent of CZMQ project .
*
* Classes of this package tend to achieve these goals:
*
* To wrap the ØMQ core API in semantics that lead to shorter, more readable applications.
* To provide a space for development of more sophisticated API semantics.
*
*/
package org.zeromq;
jeromq-0.6.0/src/main/java/org/zeromq/proto/ 0000775 0000000 0000000 00000000000 14557711263 0020730 5 ustar 00root root 0000000 0000000 jeromq-0.6.0/src/main/java/org/zeromq/proto/ZNeedle.java 0000664 0000000 0000000 00000013601 14557711263 0023122 0 ustar 00root root 0000000 0000000 package org.zeromq.proto;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.zeromq.ZFrame;
import zmq.util.Draft;
import zmq.util.Utils;
import zmq.util.Wire;
import zmq.util.function.BiFunction;
/**
* Needle for de/serialization of data within a frame.
*
* This is a DRAFT class, and may change without notice.
*/
@Draft
public final class ZNeedle
{
private final ByteBuffer needle; // Read/write pointer for serialization
public ZNeedle(ZFrame frame)
{
this(frame.getData());
}
private ZNeedle(byte[] data)
{
needle = ByteBuffer.wrap(data);
}
private void checkAvailable(int size)
{
Utils.checkArgument(needle.position() + size <= needle.limit(), () -> "Unable to handle " + size + " bytes");
}
private void forward(int size)
{
needle.position(needle.position() + size);
}
private T get(BiFunction getter, int size)
{
T value = getter.apply(needle, needle.position());
forward(size);
return value;
}
// Put a 1-byte number to the frame
public void putNumber1(int value)
{
checkAvailable(1);
needle.put((byte) (value & 0xff));
}
// Get a 1-byte number to the frame
// then make it unsigned
public int getNumber1()
{
checkAvailable(1);
int value = needle.get(needle.position()) & 0xff;
forward(1);
return value;
}
// Put a 2-byte number to the frame
public void putNumber2(int value)
{
checkAvailable(2);
Wire.putUInt16(needle, value);
}
// Get a 2-byte number to the frame
public int getNumber2()
{
checkAvailable(2);
return get(Wire::getUInt16, 2);
}
// Put a 4-byte number to the frame
public void putNumber4(int value)
{
checkAvailable(4);
Wire.putUInt32(needle, value);
}
// Get a 4-byte number to the frame
// then make it unsigned
public int getNumber4()
{
checkAvailable(4);
return get(Wire::getUInt32, 4);
}
// Put a 8-byte number to the frame
public void putNumber8(long value)
{
checkAvailable(8);
Wire.putUInt64(needle, value);
}
// Get a 8-byte number to the frame
public long getNumber8()
{
checkAvailable(8);
return get(Wire::getUInt64, 8);
}
// Put a block to the frame
public void putBlock(byte[] value, int size)
{
needle.put(value, 0, size);
}
public byte[] getBlock(int size)
{
checkAvailable(size);
byte[] value = new byte[size];
needle.get(value);
return value;
}
// Put a string to the frame
public void putShortString(String value)
{
checkAvailable(value.length() + 1);
Wire.putShortString(needle, value);
}
// Get a string from the frame
public String getShortString()
{
String value = Wire.getShortString(needle, needle.position());
forward(value.length() + 1);
return value;
}
public void putLongString(String value)
{
checkAvailable(value.length() + 4);
Wire.putLongString(needle, value);
}
// Get a long string from the frame
public String getLongString()
{
String value = Wire.getLongString(needle, needle.position());
forward(value.length() + 4);
return value;
}
// Put a string to the frame
public void putString(String value)
{
if (value.length() > Byte.MAX_VALUE * 2 + 1) {
putLongString(value);
}
else {
putShortString(value);
}
}
// Get a short string from the frame
public String getString()
{
return getShortString();
}
// Put a collection of strings to the frame
public void putList(Collection elements)
{
if (elements == null) {
putNumber1(0);
}
else {
Utils.checkArgument(elements.size() < 256, "Collection has to be smaller than 256 elements");
putNumber1(elements.size());
for (String string : elements) {
putString(string);
}
}
}
public List getList()
{
int size = getNumber1();
List list = new ArrayList<>(size);
for (int idx = 0; idx < size; ++ idx) {
list.add(getString());
}
return list;
}
// Put a map of strings to the frame
public void putMap(Map map)
{
if (map == null) {
putNumber1(0);
}
else {
Utils.checkArgument(map.size() < 256, "Map has to be smaller than 256 elements");
putNumber1(map.size());
for (Entry entry : map.entrySet()) {
if (entry.getKey().contains("=")) {
throw new IllegalArgumentException("Keys cannot contain '=' sign. " + entry);
}
if (entry.getValue().contains("=")) {
throw new IllegalArgumentException("Values cannot contain '=' sign. " + entry);
}
String val = entry.getKey() + "=" + entry.getValue();
putString(val);
}
}
}
public Map getMap()
{
int size = getNumber1();
Map map = new HashMap<>(size);
for (int idx = 0; idx < size; ++idx) {
String[] kv = getString().split("=");
assert (kv.length == 2);
map.put(kv[0], kv[1]);
}
return map;
}
@Override
public String toString()
{
return "ZNeedle [position=" + needle.position() + ", ceiling=" + needle.limit() + "]";
}
}
jeromq-0.6.0/src/main/java/org/zeromq/proto/ZPicture.java 0000664 0000000 0000000 00000040333 14557711263 0023343 0 ustar 00root root 0000000 0000000 package org.zeromq.proto;
import java.util.Locale;
import java.util.regex.Pattern;
import org.zeromq.ZFrame;
import org.zeromq.ZMQ;
import org.zeromq.ZMQ.Socket;
import org.zeromq.ZMQException;
import org.zeromq.ZMsg;
import zmq.ZError;
import zmq.util.Draft;
/**
* De/serialization of data within a message.
*
* This is a DRAFT class, and may change without notice.
*/
@Draft
public class ZPicture
{
private static final Pattern FORMAT = Pattern.compile("[i1248sbcfz]*m?");
private static final Pattern BINARY_FORMAT = Pattern.compile("[1248sSbcf]*m?");
/**
* Creates a binary encoded 'picture' message to the socket (or actor), so it can be sent.
* The arguments are encoded in a binary format that is compatible with zproto, and
* is designed to reduce memory allocations.
*
* @param picture The picture argument is a string that defines the
* type of each argument. Supports these argument types:
*
*
* Types of arguments
* pattern java type zproto type
* 1 int type = "number" size = "1"
* 2 int type = "number" size = "2"
* 4 long type = "number" size = "3"
* 8 long type = "number" size = "4"
* s String, 0-255 chars type = "string"
* S String, 0-2^32-1 chars type = "longstr"
* b byte[], 0-2^32-1 bytes type = "chunk"
* c byte[], 0-2^32-1 bytes type = "chunk"
* f ZFrame type = "frame"
* m ZMsg type = "msg" Has to be the last element of the picture
*
* @param args Arguments according to the picture
* @return a new {@link ZMsg} that encode the arguments
* @api.note Does not change or take ownership of any arguments.
*/
@Draft
public ZMsg msgBinaryPicture(String picture, Object... args)
{
if (!BINARY_FORMAT.matcher(picture).matches()) {
throw new ZMQException(picture + " is not in expected binary format " + BINARY_FORMAT.pattern(),
ZError.EPROTO);
}
ZMsg msg = new ZMsg();
// Pass 1: calculate total size of data frame
int frameSize = 0;
for (int index = 0; index < picture.length(); index++) {
char pattern = picture.charAt(index);
switch (pattern) {
case '1': {
frameSize += 1;
break;
}
case '2': {
frameSize += 2;
break;
}
case '4': {
frameSize += 4;
break;
}
case '8': {
frameSize += 8;
break;
}
case 's': {
String string = (String) args[index];
frameSize += 1 + (string != null ? string.getBytes(ZMQ.CHARSET).length : 0);
break;
}
case 'S': {
String string = (String) args[index];
frameSize += 4 + (string != null ? string.getBytes(ZMQ.CHARSET).length : 0);
break;
}
case 'b':
case 'c': {
byte[] block = (byte[]) args[index];
frameSize += 4 + block.length;
break;
}
case 'f': {
ZFrame frame = (ZFrame) args[index];
msg.add(frame);
break;
}
case 'm': {
ZMsg other = (ZMsg) args[index];
if (other == null) {
msg.add(new ZFrame((byte[]) null));
}
else {
msg.addAll(other);
}
break;
}
default:
assert (false) : "invalid picture element '" + pattern + "'";
}
}
// Pass 2: encode data into data frame
ZFrame frame = new ZFrame(new byte[frameSize]);
ZNeedle needle = new ZNeedle(frame);
for (int index = 0; index < picture.length(); index++) {
char pattern = picture.charAt(index);
switch (pattern) {
case '1': {
needle.putNumber1((int) args[index]);
break;
}
case '2': {
needle.putNumber2((int) args[index]);
break;
}
case '4': {
needle.putNumber4((int) args[index]);
break;
}
case '8': {
needle.putNumber8((long) args[index]);
break;
}
case 's': {
needle.putString((String) args[index]);
break;
}
case 'S': {
needle.putLongString((String) args[index]);
break;
}
case 'b':
case 'c': {
byte[] block = (byte[]) args[index];
needle.putNumber4(block.length);
needle.putBlock(block, block.length);
break;
}
case 'f':
case 'm':
break;
default:
assert (false) : "invalid picture element '" + pattern + "'";
}
}
msg.addFirst(frame);
return msg;
}
@Draft
public boolean sendBinaryPicture(Socket socket, String picture, Object... args)
{
return msgBinaryPicture(picture, args).send(socket);
}
/**
* Receive a binary encoded 'picture' message from the socket (or actor).
* This method is similar to {@link org.zeromq.ZMQ.Socket#recv()}, except the arguments are encoded
* in a binary format that is compatible with zproto, and is designed to
* reduce memory allocations.
*
* @param picture The picture argument is a string that defines
* the type of each argument. See {@link #sendBinaryPicture(Socket, String, Object...)}
* for the supported argument types.
* @return the picture elements as object array
**/
@Draft
public Object[] recvBinaryPicture(Socket socket, final String picture)
{
if (!BINARY_FORMAT.matcher(picture).matches()) {
throw new ZMQException(picture + " is not in expected binary format " + BINARY_FORMAT.pattern(),
ZError.EPROTO);
}
ZFrame frame = ZFrame.recvFrame(socket);
if (frame == null) {
return null;
}
// Get the data frame
ZNeedle needle = new ZNeedle(frame);
Object[] results = new Object[picture.length()];
for (int index = 0; index < picture.length(); index++) {
char pattern = picture.charAt(index);
switch (pattern) {
case '1': {
results[index] = needle.getNumber1();
break;
}
case '2': {
results[index] = needle.getNumber2();
break;
}
case '4': {
results[index] = needle.getNumber4();
break;
}
case '8': {
results[index] = needle.getNumber8();
break;
}
case 's': {
results[index] = needle.getString();
break;
}
case 'S': {
results[index] = needle.getLongString();
break;
}
case 'b':
case 'c': {
int size = needle.getNumber4();
results[index] = needle.getBlock(size);
break;
}
case 'f': {
// Get next frame off socket
results[index] = ZFrame.recvFrame(socket);
break;
}
case 'm': {
// Get zero or more remaining frames
results[index] = ZMsg.recvMsg(socket);
break;
}
default:
assert (false) : "invalid picture element '" + pattern + "'";
}
}
return results;
}
/**
* Queues a 'picture' message to the socket (or actor), so it can be sent.
*
* @param picture The picture is a string that defines the type of each frame.
* This makes it easy to send a complex multiframe message in
* one call. The picture can contain any of these characters,
* each corresponding to zero or one arguments:
*
*
*
* Types of arguments
* i = int (stores signed integer)
* 1 = byte (stores 8-bit unsigned integer)
* 2 = int (stores 16-bit unsigned integer)
* 4 = long (stores 32-bit unsigned integer)
* 8 = long (stores 64-bit unsigned integer)
* s = String
* b = byte[]
* c = byte[]
* f = ZFrame
* m = ZMsg (sends all frames in the ZMsg)Has to be the last element of the picture
* z = sends zero-sized frame (0 arguments)
*
* Note that s, b, f and m are encoded the same way and the choice is
* offered as a convenience to the sender, which may or may not already
* have data in a ZFrame or ZMsg. Does not change or take ownership of
* any arguments.
*
* Also see {@link #recvPicture(Socket, String)}} how to recv a
* multiframe picture.
* @param args Arguments according to the picture
* @return true if successful, false if sending failed for any reason
*/
@Draft
public boolean sendPicture(Socket socket, String picture, Object... args)
{
if (!FORMAT.matcher(picture).matches()) {
throw new ZMQException(picture + " is not in expected format " + FORMAT.pattern(), ZError.EPROTO);
}
ZMsg msg = new ZMsg();
for (int pictureIndex = 0, argIndex = 0; pictureIndex < picture.length(); pictureIndex++, argIndex++) {
char pattern = picture.charAt(pictureIndex);
switch (pattern) {
case 'i': {
msg.add(String.format(Locale.ENGLISH, "%d", (int) args[argIndex]));
break;
}
case '1': {
msg.add(String.format(Locale.ENGLISH, "%d", (0xff) & (int) args[argIndex]));
break;
}
case '2': {
msg.add(String.format(Locale.ENGLISH, "%d", (0xffff) & (int) args[argIndex]));
break;
}
case '4': {
msg.add(String.format(Locale.ENGLISH, "%d", (0xffffffff) & (int) args[argIndex]));
break;
}
case '8': {
msg.add(String.format(Locale.ENGLISH, "%d", (long) args[argIndex]));
break;
}
case 's': {
msg.add((String) args[argIndex]);
break;
}
case 'b':
case 'c': {
msg.add((byte[]) args[argIndex]);
break;
}
case 'f': {
msg.add((ZFrame) args[argIndex]);
break;
}
case 'm': {
ZMsg msgParm = (ZMsg) args[argIndex];
while (!msgParm.isEmpty()) {
msg.add(msgParm.pop());
}
break;
}
case 'z': {
msg.add((byte[]) null);
argIndex--;
break;
}
default:
assert (false) : "invalid picture element '" + pattern + "'";
}
}
return msg.send(socket, false);
}
/**
* Receive a 'picture' message to the socket (or actor).
*
*
* @param picture The picture is a string that defines the type of each frame.
* This makes it easy to recv a complex multiframe message in
* one call. The picture can contain any of these characters,
* each corresponding to zero or one elements in the result:
*
*
*
* Types of arguments
* i = int (stores signed integer)
* 1 = int (stores 8-bit unsigned integer)
* 2 = int (stores 16-bit unsigned integer)
* 4 = long (stores 32-bit unsigned integer)
* 8 = long (stores 64-bit unsigned integer)
* s = String
* b = byte[]
* f = ZFrame (creates zframe)
* m = ZMsg (creates a zmsg with the remaing frames)
* z = null, asserts empty frame (0 arguments)
*
*
* Also see {@link #sendPicture(Socket, String, Object...)} how to send a
* multiframe picture.
*
* @return the picture elements as object array
*/
@Draft
public Object[] recvPicture(Socket socket, String picture)
{
if (!FORMAT.matcher(picture).matches()) {
throw new ZMQException(picture + " is not in expected format " + FORMAT.pattern(), ZError.EPROTO);
}
Object[] elements = new Object[picture.length()];
for (int index = 0; index < picture.length(); index++) {
char pattern = picture.charAt(index);
switch (pattern) {
case 'i': {
elements[index] = Integer.valueOf(socket.recvStr());
break;
}
case '1': {
elements[index] = (0xff) & Integer.parseInt(socket.recvStr());
break;
}
case '2': {
elements[index] = (0xffff) & Integer.parseInt(socket.recvStr());
break;
}
case '4': {
elements[index] = (0xffffffff) & Integer.parseInt(socket.recvStr());
break;
}
case '8': {
elements[index] = Long.valueOf(socket.recvStr());
break;
}
case 's': {
elements[index] = socket.recvStr();
break;
}
case 'b':
case 'c': {
elements[index] = socket.recv();
break;
}
case 'f': {
elements[index] = ZFrame.recvFrame(socket);
break;
}
case 'm': {
elements[index] = ZMsg.recvMsg(socket);
break;
}
case 'z': {
ZFrame zeroFrame = ZFrame.recvFrame(socket);
if (zeroFrame == null || zeroFrame.size() > 0) {
throw new ZMQException("zero frame is not empty", ZError.EPROTO);
}
elements[index] = new ZFrame((byte[]) null);
break;
}
default:
assert (false) : "invalid picture element '" + pattern + "'";
}
}
return elements;
}
}
jeromq-0.6.0/src/main/java/org/zeromq/proto/package-info.java 0000664 0000000 0000000 00000000126 14557711263 0024116 0 ustar 00root root 0000000 0000000 /**
* Provides utility classes for ØMQ zproto.
*/
package org.zeromq.proto;
jeromq-0.6.0/src/main/java/org/zeromq/timer/ 0000775 0000000 0000000 00000000000 14557711263 0020705 5 ustar 00root root 0000000 0000000 jeromq-0.6.0/src/main/java/org/zeromq/timer/TimerHandler.java 0000664 0000000 0000000 00000000462 14557711263 0024130 0 ustar 00root root 0000000 0000000 package org.zeromq.timer;
import zmq.util.Draft;
import zmq.util.Timers;
/**
* Called when the time has come to perform some action.
* This is a DRAFT class, and may change without notice.
*/
@Draft
public interface TimerHandler extends Timers.Handler
{
@Override
void time(Object... args);
}
jeromq-0.6.0/src/main/java/org/zeromq/timer/ZTicker.java 0000664 0000000 0000000 00000003332 14557711263 0023124 0 ustar 00root root 0000000 0000000 package org.zeromq.timer;
import org.zeromq.timer.ZTicket.Ticket;
import org.zeromq.timer.ZTimer.Timer;
import zmq.util.Draft;
import zmq.util.function.Supplier;
/**
* Manages set of tickets and timers.
*
* Tickets can be added with a given delay in milliseconds,
* when the delay expires after addition,
* handler method is executed with given arguments.
*
* Ticket is NOT repetitive and will be executed once unless canceled.
*
* Timers can be added with a given interval in milliseconds,
* when the interval of time expires after addition,
* handler method is executed with given arguments.
*
* Timer is repetitive and will be executed over time until canceled.
*
* This is a DRAFT class, and may change without notice.
*/
@Draft
public final class ZTicker
{
private final ZTimer timer;
private final ZTicket ticket;
public ZTicker()
{
timer = new ZTimer();
ticket = new ZTicket();
}
ZTicker(Supplier clock)
{
timer = new ZTimer(clock);
ticket = new ZTicket(clock);
}
public Ticket addTicket(long interval, TimerHandler handler, Object... args)
{
return ticket.add(interval, handler, args);
}
public Timer addTimer(long interval, TimerHandler handler, Object... args)
{
return timer.add(interval, handler, args);
}
public long timeout()
{
long timer = this.timer.timeout();
long ticket = this.ticket.timeout();
if (timer < 0 || ticket < 0) {
return Math.max(timer, ticket);
}
else {
return Math.min(timer, ticket);
}
}
public int execute()
{
return timer.execute() + ticket.execute();
}
}
jeromq-0.6.0/src/main/java/org/zeromq/timer/ZTicket.java 0000664 0000000 0000000 00000013666 14557711263 0023141 0 ustar 00root root 0000000 0000000 package org.zeromq.timer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import zmq.util.Clock;
import zmq.util.Draft;
import zmq.util.Utils;
import zmq.util.function.Supplier;
/**
* Manages set of tickets.
*
* Ticket timers are very fast in the case where
* you use a lot of timers (thousands), and frequently remove and add them.
* The main use case is expiry timers for servers that handle many clients,
* and which reset the expiry timer for each message received from a client.
* Whereas normal timers perform poorly as the number of clients grows, the
* cost of ticket timers is constant, no matter the number of clients
*
* Tickets can be added with a given delay.
*
* When the delay of time expires after addition, handler method is executed with given arguments.
*
* Ticket is NOT repetitive and will be executed once unless canceled.
*
* This class is not thread-safe
*
* This is a DRAFT class, and may change without notice.
*/
@Draft
public final class ZTicket
{
/**
* Opaque representation of a ticket.
*/
public static final class Ticket implements Comparable
{
private final ZTicket parent;
private final TimerHandler handler;
private final Object[] args;
private long start;
private long delay;
private boolean alive = true;
private Ticket(ZTicket parent, long now, long delay, TimerHandler handler, Object... args)
{
assert (args != null);
this.parent = parent;
this.start = now;
this.delay = delay;
this.handler = handler;
this.args = args;
}
/**
* Resets the ticket.
*/
public void reset()
{
if (alive) {
parent.sort = true;
start = parent.now();
}
}
/**
* Cancels a ticket.
* @return true if cancelled, false if already cancelled.
*/
public boolean cancel()
{
if (alive) {
alive = false;
parent.sort = true;
return true;
}
return false;
}
/**
* Changes the delay of the ticket.
* @param delay the new delay of the ticket.
*/
public void setDelay(long delay)
{
if (alive) {
parent.sort = true;
this.delay = delay;
}
}
@Override
public int compareTo(Ticket other)
{
if (alive) {
if (other.alive) {
return Long.compare(start - other.start, other.delay - delay);
}
return -1;
}
return other.alive ? 1 : 0;
}
}
private final List tickets;
private final Supplier clock;
private boolean sort;
public ZTicket()
{
this(() -> TimeUnit.NANOSECONDS.toMillis(Clock.nowNS()));
}
ZTicket(Supplier clock)
{
this(clock, new ArrayList<>());
}
ZTicket(Supplier clock, List tickets)
{
this.clock = clock;
this.tickets = tickets;
}
private long now()
{
return clock.get();
}
private void insert(Ticket ticket)
{
sort = tickets.add(ticket);
}
/**
* Add ticket to the set.
* @param delay the expiration delay in milliseconds.
* @param handler the callback called at the expiration of the ticket.
* @param args the optional arguments for the handler.
* @return an opaque handle for further cancel and reset.
*/
public Ticket add(long delay, TimerHandler handler, Object... args)
{
if (handler == null) {
return null;
}
Utils.checkArgument(delay > 0, "Delay of a ticket has to be strictly greater than 0");
final Ticket ticket = new Ticket(this, now(), delay, handler, args);
insert(ticket);
return ticket;
}
/**
* Returns the time in millisecond until the next ticket.
* @return the time in millisecond until the next ticket.
*/
public long timeout()
{
if (tickets.isEmpty()) {
return -1;
}
sortIfNeeded();
// Tickets are sorted, so check first ticket
Ticket first = tickets.get(0);
long time = first.start - now() + first.delay;
if (time > 0) {
return time;
}
else {
return 0;
}
}
/**
* Execute the tickets.
* @return the number of tickets triggered.
*/
public int execute()
{
int executed = 0;
final long now = now();
sortIfNeeded();
Set cancelled = new HashSet<>();
for (Ticket ticket : this.tickets) {
if (now - ticket.start < ticket.delay) {
// tickets are ordered, not meeting the condition means the next ones do not as well
break;
}
if (!ticket.alive) {
// Dead ticket, let's continue
cancelled.add(ticket);
continue;
}
ticket.alive = false;
cancelled.add(ticket);
ticket.handler.time(ticket.args);
++executed;
}
for (int idx = tickets.size(); idx-- > 0; ) {
Ticket ticket = tickets.get(idx);
if (ticket.alive) {
break;
}
cancelled.add(ticket);
}
this.tickets.removeAll(cancelled);
cancelled.clear();
return executed;
}
private void sortIfNeeded()
{
if (sort) {
sort = false;
Collections.sort(tickets);
}
}
}
jeromq-0.6.0/src/main/java/org/zeromq/timer/ZTimer.java 0000664 0000000 0000000 00000005667 14557711263 0023000 0 ustar 00root root 0000000 0000000 package org.zeromq.timer;
import zmq.util.Draft;
import zmq.util.Timers;
import zmq.util.function.Supplier;
/**
* Manages set of timers.
*
* Timers can be added with a given interval, when the interval of time expires after addition, handler method is executed with given arguments.
* Timer is repetitive and will be executed over time until canceled.
*
* This is a DRAFT class, and may change without notice.
*/
@Draft
public final class ZTimer
{
/**
* Opaque representation of a timer.
*/
public static final class Timer
{
private final Timers.Timer delegate;
Timer(Timers.Timer delegate)
{
this.delegate = delegate;
}
/**
* Changes the interval of the timer.
*
* This method is slow, canceling existing and adding a new timer yield better performance.
* @param interval the new interval of the time.
* @return true if set, otherwise false.
*/
public boolean setInterval(long interval)
{
return delegate.setInterval(interval);
}
/**
* Reset the timer.
*
* This method is slow, canceling existing and adding a new timer yield better performance.
* @return true if reset, otherwise false.
*/
public boolean reset()
{
return delegate.reset();
}
/**
* Cancels a timer.
*
* @return true if cancelled, otherwise false.
*/
public boolean cancel()
{
return delegate.cancel();
}
}
private final Timers timer;
public ZTimer()
{
timer = new Timers();
}
ZTimer(Supplier clock)
{
timer = new Timers(clock);
}
/**
* Add timer to the set, timer repeats forever, or until cancel is called.
* @param interval the interval of repetition in milliseconds.
* @param handler the callback called at the expiration of the timer.
* @param args the optional arguments for the handler.
* @return an opaque handle for further cancel.
*/
public Timer add(long interval, TimerHandler handler, Object... args)
{
if (handler == null) {
return null;
}
return new Timer(timer.add(interval, handler, args));
}
/**
* Returns the time in millisecond until the next timer.
*
* @return the time in millisecond until the next timer.
*/
public long timeout()
{
return timer.timeout();
}
/**
* Execute the timers.
*
* @return the number of timers triggered.
*/
public int execute()
{
return timer.execute();
}
/**
* Sleeps until at least one timer can be executed and execute the timers.
*
* @return the number of timers triggered.
*/
public int sleepAndExecute()
{
return timer.sleepAndExecute();
}
}
jeromq-0.6.0/src/main/java/org/zeromq/timer/package-info.java 0000664 0000000 0000000 00000000126 14557711263 0024073 0 ustar 00root root 0000000 0000000 /**
* Provides timing utility classes for ØMQ.
*/
package org.zeromq.timer;
jeromq-0.6.0/src/main/java/org/zeromq/util/ 0000775 0000000 0000000 00000000000 14557711263 0020542 5 ustar 00root root 0000000 0000000 jeromq-0.6.0/src/main/java/org/zeromq/util/ZData.java 0000664 0000000 0000000 00000007551 14557711263 0022420 0 ustar 00root root 0000000 0000000 package org.zeromq.util;
import java.io.PrintStream;
import java.util.Arrays;
import org.zeromq.ZMQ;
public class ZData
{
private static final String HEX_CHAR = "0123456789ABCDEF";
private final byte[] data;
public ZData(byte[] data)
{
this.data = data;
}
/**
* String equals.
* Uses String compareTo for the comparison (lexigraphical)
* @param str String to compare with data
* @return True if data matches given string
*/
public boolean streq(String str)
{
return streq(data, str);
}
/**
* String equals.
* Uses String compareTo for the comparison (lexigraphical)
* @param str String to compare with data
* @param data the binary data to compare
* @return True if data matches given string
*/
public static boolean streq(byte[] data, String str)
{
if (data == null) {
return false;
}
return new String(data, ZMQ.CHARSET).compareTo(str) == 0;
}
public boolean equals(byte[] that)
{
return Arrays.equals(data, that);
}
@Override
public boolean equals(Object other)
{
if (this == other) {
return true;
}
if (other == null) {
return false;
}
if (getClass() != other.getClass()) {
return false;
}
ZData that = (ZData) other;
return Arrays.equals(this.data, that.data);
}
@Override
public int hashCode()
{
return Arrays.hashCode(data);
}
/**
* Returns a human - readable representation of data
* @return
* A text string or hex-encoded string if data contains any non-printable ASCII characters
*/
public String toString()
{
return toString(data);
}
public static String toString(byte[] data)
{
if (data == null) {
return "";
}
// Dump message as text or hex-encoded string
boolean isText = true;
for (byte aData : data) {
if (aData < 32 || aData > 127) {
isText = false;
break;
}
}
if (isText) {
return new String(data, ZMQ.CHARSET);
}
else {
return strhex(data);
}
}
/**
* @return data as a printable hex string
*/
public String strhex()
{
return strhex(data);
}
public static String strhex(byte[] data)
{
if (data == null) {
return "";
}
StringBuilder b = new StringBuilder(data.length * 2);
for (byte aData : data) {
int b1 = aData >>> 4 & 0xf;
int b2 = aData & 0xf;
b.append(HEX_CHAR.charAt(b1));
b.append(HEX_CHAR.charAt(b2));
}
return b.toString();
}
public void print(PrintStream out, String prefix)
{
print(out, prefix, data, data.length);
}
public static void print(PrintStream out, String prefix, byte[] data, int size)
{
if (data == null) {
return;
}
if (prefix != null) {
out.printf("%s", prefix);
}
boolean isBin = false;
int charNbr;
for (charNbr = 0; charNbr < size; charNbr++) {
if (data[charNbr] < 9 || data[charNbr] > 127) {
isBin = true;
}
}
out.printf("[%03d] ", size);
int maxSize = isBin ? 35 : 70;
String elipsis = "";
if (size > maxSize) {
size = maxSize;
elipsis = "...";
}
for (charNbr = 0; charNbr < size; charNbr++) {
if (isBin) {
out.printf("%02X", data[charNbr]);
}
else {
out.printf("%c", data[charNbr]);
}
}
out.printf("%s\n", elipsis);
out.flush();
}
}
jeromq-0.6.0/src/main/java/org/zeromq/util/ZDigest.java 0000664 0000000 0000000 00000003055 14557711263 0022761 0 ustar 00root root 0000000 0000000 package org.zeromq.util;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* The ZDigest class generates a hash from chunks of data. The current algorithm is SHA-1, chosen for speed.
*/
public class ZDigest
{
private final byte[] buffer;
private final MessageDigest sha1;
/**
* Creates a new digester.
*/
public ZDigest()
{
this(new byte[8192]);
}
/**
* Creates a new digester.
* @param buffer the temp buffer used for computation of streams.
*/
public ZDigest(byte[] buffer)
{
this.buffer = buffer;
try {
sha1 = MessageDigest.getInstance("SHA-1");
}
catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
public ZDigest update(InputStream input) throws IOException
{
int read = input.read(buffer);
while (read != -1) {
sha1.update(buffer, 0, read);
read = input.read(buffer);
}
return this;
}
public ZDigest update(byte[] input)
{
return update(input, 0, input.length);
}
public ZDigest update(byte[] input, int offset, int length)
{
sha1.update(input, offset, length);
return this;
}
public byte[] data()
{
return sha1.digest();
}
public int size()
{
return sha1.digest().length;
}
public String string()
{
return ZData.toString(data());
}
}
jeromq-0.6.0/src/main/java/org/zeromq/util/ZMetadata.java 0000664 0000000 0000000 00000013652 14557711263 0023266 0 ustar 00root root 0000000 0000000 package org.zeromq.util;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.util.Collection;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import org.zeromq.ZConfig;
import org.zeromq.ZMQ;
import zmq.io.Metadata;
public class ZMetadata
{
private final Metadata metadata;
public ZMetadata()
{
this(new Metadata());
}
public ZMetadata(Metadata metadata)
{
this.metadata = metadata;
}
public Set keySet()
{
return metadata.keySet();
}
public String get(String key)
{
return metadata.get(key);
}
public void set(String key, String value)
{
metadata.put(key, value);
}
public void remove(String key)
{
metadata.remove(key);
}
public byte[] bytes()
{
return metadata.bytes();
}
public String toString()
{
return metadata.toString();
}
public int hashCode()
{
return metadata.hashCode();
}
@Override
public boolean equals(Object o)
{
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ZMetadata zMetadata = (ZMetadata) o;
return Objects.equals(metadata, zMetadata.metadata);
}
/**
* Returns a {@link Set} view of the properties contained in this metadata.
* The set is backed by the metadata, so changes to the metadata are
* reflected in the set, and vice-versa. If the metadata is modified
* while an iteration over the set is in progress (except through
* the iterator's own {@code remove} operation, or through the
* {@code setValue} operation on a metadata property returned by the
* iterator) the results of the iteration are undefined. The set
* supports element removal, which removes the corresponding
* mapping from the metadata, via the {@code Iterator.remove},
* {@code Set.remove}, {@code removeAll}, {@code retainAll} and
* {@code clear} operations. It does not support the
* {@code add} or {@code addAll} operations.
*
* @return a set view of the properties contained in this metadata
*/
public Set> entrySet()
{
return metadata.entrySet();
}
/**
* Returns a {@link Collection} view of the values contained in this metadata.
* The collection is backed by the metadata, so changes to the map are
* reflected in the collection, and vice-versa. If the metadata is
* modified while an iteration over the collection is in progress
* (except through the iterator's own {@code remove} operation),
* the results of the iteration are undefined. The collection
* supports element removal, which removes the corresponding
* property from the metadata, via the {@code Iterator.remove},
* {@code Collection.remove}, {@code removeAll},
* {@code retainAll} and {@code clear} operations. It does not
* support the {@code add} or {@code addAll} operations.
*
* @return a collection view of the values contained in this map
*/
public Collection values()
{
return metadata.values();
}
public void set(Metadata zapProperties)
{
metadata.set(zapProperties);
}
/**
* Returns {@code true} if this map contains no key-value mappings.
*
* @return {@code true} if this map contains no key-value mappings
*/
public boolean isEmpty()
{
return metadata.isEmpty();
}
/**
* Returns {@code true} if this metada contains the property requested
*
* @param property property the name of the property to be tested.
* @return {@code true} if this metada contains the property
*/
public boolean containsKey(String property)
{
return metadata.containsKey(property);
}
/**
* Removes all the properties.
* The map will be empty after this call returns.
*/
public void clear()
{
metadata.clear();
}
/**
* Returns the number of properties. If it contains more properties
* than {@code Integer.MAX_VALUE} elements, returns {@code Integer.MAX_VALUE}.
*
* @return the number of properties
*/
public int size()
{
return metadata.size();
}
/**
* Serialize metadata to an output stream, using the specifications of the ZMTP protocol
*
* property = name value
* name = OCTET 1*255name-char
* name-char = ALPHA | DIGIT | "-" | "_" | "." | "+"
* value = 4OCTET *OCTET ; Size in network byte order
*
*
* @param stream the output stream
* @throws IOException if an I/O error occurs.
* @throws IllegalStateException if one of the properties name size is bigger than 255
*/
public void write(OutputStream stream) throws IOException
{
metadata.write(stream);
}
public static ZMetadata read(String meta)
{
if (meta == null || meta.isEmpty()) {
return null;
}
try {
ByteBuffer buffer = ZMQ.CHARSET.newEncoder().encode(CharBuffer.wrap(meta));
Metadata data = new Metadata();
data.read(buffer, 0, null);
return new ZMetadata(data);
}
catch (CharacterCodingException e) {
throw new IllegalArgumentException("Not a parsable metadata string");
}
}
public static ZMetadata read(ZConfig conf)
{
ZConfig meta = conf.getChild("metadata");
if (meta == null) {
return null;
}
ZMetadata metadata = new ZMetadata();
for (Entry entry : meta.getValues().entrySet()) {
metadata.set(entry.getKey(), entry.getValue());
}
return metadata;
}
}
jeromq-0.6.0/src/main/java/org/zeromq/util/package-info.java 0000664 0000000 0000000 00000001047 14557711263 0023733 0 ustar 00root root 0000000 0000000 /**
* Provides high-level utility bindings for ØMQ.
*
* This is the java equivalent of CZMQ project ,
* but packaged separately to avoid proliferation of classes under the same namespace.
*
* Classes of this package tend to achieve these goals:
*
* To wrap the ØMQ core API in semantics that lead to shorter, more readable applications.
* To provide a space for development of more sophisticated API semantics.
*
*
*/
package org.zeromq.util;
jeromq-0.6.0/src/main/java/zmq/ 0000775 0000000 0000000 00000000000 14557711263 0016270 5 ustar 00root root 0000000 0000000 jeromq-0.6.0/src/main/java/zmq/Command.java 0000664 0000000 0000000 00000005660 14557711263 0020520 0 ustar 00root root 0000000 0000000 package zmq;
// This structure defines the commands that can be sent between threads.
public class Command
{
// Object to process the command.
final ZObject destination;
final Type type;
final Object arg;
public enum Type
{
// Sent to I/O thread to let it know that it should
// terminate itself.
STOP,
// Sent to I/O object to make it register with its I/O thread
PLUG,
// Sent to socket to let it know about the newly created object.
OWN,
// Attach the engine to the session. If engine is NULL, it informs
// session that the connection have failed.
ATTACH,
// Sent from session to socket to establish pipe(s) between them.
// Caller have used inc_seqnum beforehand sending the command.
BIND,
// Sent by pipe writer to inform dormant pipe reader that there
// are messages in the pipe.
ACTIVATE_READ,
// Sent by pipe reader to inform pipe writer about how many
// messages it has read so far.
ACTIVATE_WRITE,
// Sent by pipe reader to writer after creating a new inpipe.
// The parameter is actually of type pipe_t::upipe_t, however,
// its definition is private so we'll have to do with void*.
HICCUP,
// Sent by pipe reader to pipe writer to ask it to terminate
// its end of the pipe.
PIPE_TERM,
// Pipe writer acknowledges pipe_term command.
PIPE_TERM_ACK,
// Sent by I/O object ot the socket to request the shutdown of
// the I/O object.
TERM_REQ,
// Sent by socket to I/O object to start its shutdown.
TERM,
// Sent by I/O object to the socket to acknowledge it has
// shut down.
TERM_ACK,
// Transfers the ownership of the closed socket
// to the reaper thread.
REAP,
// Sent by non-reaper socket to reaper to check destroy.
REAP_ACK,
// Closed socket notifies the reaper that it's already deallocated.
REAPED,
// TODO V4 provide a description for Command#INPROC_CONNECTED
INPROC_CONNECTED,
// Sent by reaper thread to the term thread when all the sockets
// are successfully deallocated.
DONE,
// Cancel a single pending I/O call
CANCEL
}
Command(ZObject destination, Type type)
{
this(destination, type, null);
}
Command(ZObject destination, Type type, Object arg)
{
this.destination = destination;
this.type = type;
this.arg = arg;
}
public final void process()
{
destination.processCommand(this);
}
@Override
public String toString()
{
return "Cmd" + "[" + destination + ", " + (destination == null ? "Reaper" : destination.getTid() + ", ") + type
+ (arg == null ? "" : ", " + arg) + "]";
}
}
jeromq-0.6.0/src/main/java/zmq/Config.java 0000664 0000000 0000000 00000006044 14557711263 0020344 0 ustar 00root root 0000000 0000000 package zmq;
public enum Config
{
// Number of new messages in message pipe needed to trigger new memory
// allocation. Setting this parameter to 256 decreases the impact of
// memory allocation by approximately 99.6%
MESSAGE_PIPE_GRANULARITY(256),
// Commands in pipe per allocation event.
COMMAND_PIPE_GRANULARITY(16),
// Determines how often does socket poll for new commands when it
// still has unprocessed messages to handle. Thus, if it is set to 100,
// socket will process 100 inbound messages before doing the poll.
// If there are no unprocessed messages available, poll is done
// immediately. Decreasing the value trades overall latency for more
// real-time behaviour (less latency peaks).
INBOUND_POLL_RATE(100),
// Maximal batching size for engines with receiving functionality.
// So, if there are 10 messages that fit into the batch size, all of
// them may be read by a single 'recv' system call, thus avoiding
// unnecessary network stack traversals.
IN_BATCH_SIZE(8192),
// Maximal batching size for engines with sending functionality.
// So, if there are 10 messages that fit into the batch size, all of
// them may be written by a single 'send' system call, thus avoiding
// unnecessary network stack traversals.
OUT_BATCH_SIZE(8192),
// Maximal delta between high and low watermark.
MAX_WM_DELTA(1024),
// Maximum number of events the I/O thread can process in one go.
MAX_IO_EVENTS(256),
// Maximal delay to process command in API thread (in CPU ticks).
// 3,000,000 ticks equals to 1 - 2 milliseconds on current CPUs.
// Note that delay is only applied when there is continuous stream of
// messages to process. If not so, commands are processed immediately.
MAX_COMMAND_DELAY(3000000),
// Low-precision clock precision in CPU ticks. 1ms. Value of 1000000
// should be OK for CPU frequencies above 1GHz. If should work
// reasonably well for CPU frequencies above 500MHz. For lower CPU
// frequencies you may consider lowering this value to get best
// possible latencies.
CLOCK_PRECISION(1000000),
// Maximum transport data unit size for PGM (TPDU).
PGM_MAX_TPDU(1500),
// On some OSes the signaler has to be emulated using a TCP
// connection. In such cases following port is used.
// If 0, it lets the OS choose a free port without requiring use of a
// global mutex. The original implementation of a Windows signaler
// socket used port 5905 instead of letting the OS choose a free port.
// https://github.com/zeromq/libzmq/issues/1542
SIGNALER_PORT(0),
// Threshold of message size for choosing if message data should
// be allocated on heap or on direct memory.
// This has a direct impact on transmission of large messages
MSG_ALLOCATION_HEAP_THRESHOLD(1024 * 1024);
private final int value;
Config(int value)
{
this.value = value;
}
public int getValue()
{
return value;
}
}
jeromq-0.6.0/src/main/java/zmq/Ctx.java 0000664 0000000 0000000 00000072260 14557711263 0017700 0 ustar 00root root 0000000 0000000 package zmq;
import java.io.IOException;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.nio.channels.SelectableChannel;
import java.nio.channels.Selector;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.zeromq.ZMQException;
import zmq.io.IOThread;
import zmq.pipe.Pipe;
import zmq.socket.Sockets;
import zmq.util.Errno;
import zmq.util.MultiMap;
import zmq.util.function.BiFunction;
/**
* Context object encapsulates all the global state associated with
* the library.
* It creates a reaper thread and some IO threads as defined by {@link ZMQ#ZMQ_IO_THREADS}. The thread are created
* using a thread factory that defined the UncaughtExceptionHandler as defined by {@link Ctx#setUncaughtExceptionHandler(UncaughtExceptionHandler)}
* and defined the thread as a daemon. If a custom thread factory is defined with {@link Ctx#setThreadFactory(BiFunction)},
* all that steps must be handled manually.
*/
public class Ctx
{
private static final int WAIT_FOREVER = -1;
// Information associated with inproc endpoint. Note that endpoint options
// are registered as well so that the peer can access them without a need
// for synchronization, handshaking or similar.
public static class Endpoint
{
public final SocketBase socket;
public final Options options;
public Endpoint(SocketBase socket, Options options)
{
this.socket = socket;
this.options = options;
}
}
private static class PendingConnection
{
private final Endpoint endpoint;
private final Pipe connectPipe;
private final Pipe bindPipe;
public PendingConnection(Endpoint endpoint, Pipe connectPipe, Pipe bindPipe)
{
super();
this.endpoint = endpoint;
this.connectPipe = connectPipe;
this.bindPipe = bindPipe;
}
}
private enum Side
{
CONNECT,
BIND
}
// Used to check whether the object is a context.
private boolean active;
// Sockets belonging to this context. We need the list so that
// we can notify the sockets when zmq_term() is called. The sockets
// will return ETERM then.
private final List sockets;
// List of unused thread slots.
private final Deque emptySlots;
// If true, init has been called but no socket has been created
// yet. Launching of I/O threads is delayed.
private final AtomicBoolean starting = new AtomicBoolean(true);
// If true, zmq_term was already called.
private volatile boolean terminating;
// Synchronization of accesses to global slot-related data:
// sockets, emptySlots, terminating. It also synchronizes
// access to zombie sockets as such (as opposed to slots) and provides
// a memory barrier to ensure that all CPU cores see the same data.
private final Lock slotSync;
// A list of poll selectors opened under this context. When the context is
// destroyed, each of the selectors is closed to ensure resource
// deallocation.
private final List selectors = new ArrayList<>();
// The reaper thread.
private Reaper reaper;
// I/O threads.
private final List ioThreads;
// Array of pointers to mailboxes for both application and I/O threads.
private int slotCount;
private IMailbox[] slots;
// Mailbox for zmq_term thread.
private final Mailbox termMailbox;
// List of inproc endpoints within this context.
private final Map endpoints;
// Synchronization of access to the list of inproc endpoints.
private final Lock endpointsSync;
// Maximum socket ID.
private static final AtomicInteger maxSocketId = new AtomicInteger(0);
// Maximum number of sockets that can be opened at the same time.
private int maxSockets;
// Number of I/O threads to launch.
private int ioThreadCount;
// The thread factory used by the poller
private BiFunction threadFactory;
// Does context wait (possibly forever) on termination?
private boolean blocky;
// Synchronization of access to context options.
private final Lock optSync;
// Synchronization of access to selectors.
private final Lock selectorSync = new ReentrantLock();
static final int TERM_TID = 0;
private static final int REAPER_TID = 1;
private final MultiMap pendingConnections = new MultiMap<>();
private boolean ipv6;
private final Errno errno = new Errno();
// Exception handlers to receive notifications of critical exceptions in zmq.poll.Poller and handle uncaught exceptions
private UncaughtExceptionHandler exhandler = Thread.getDefaultUncaughtExceptionHandler();
// Exception handlers to receive notifications of exception in zmq.poll.Poller, and can be used for logging
private UncaughtExceptionHandler exnotification = (t, e) -> e.printStackTrace();
/**
* A class that holds the informations needed to forward channel in monitor sockets.
* Of course, it only works with inproc sockets.
*
* It uses WeakReference to avoid holding references to channel if the monitor event is
* lost.
*
* A class is used as a lock in lazy allocation of the needed objects.
*/
private static class ChannelForwardHolder
{
private final AtomicInteger handleSource = new AtomicInteger(0);
private final Map> map = new ConcurrentHashMap<>();
// The WeakReference is empty when the reference is empty, so keep a reverse empty to clean the direct map.
private final Map, Integer> reversemap = new ConcurrentHashMap<>();
private final ReferenceQueue queue = new ReferenceQueue<>();
}
private ChannelForwardHolder forwardHolder = null;
public Ctx()
{
active = true;
terminating = false;
reaper = null;
slotCount = 0;
slots = null;
maxSockets = ZMQ.ZMQ_MAX_SOCKETS_DFLT;
ioThreadCount = ZMQ.ZMQ_IO_THREADS_DFLT;
threadFactory = this::createThread;
ipv6 = false;
blocky = true;
slotSync = new ReentrantLock();
endpointsSync = new ReentrantLock();
optSync = new ReentrantLock();
termMailbox = new Mailbox(this, "terminater", -1);
emptySlots = new ArrayDeque<>();
ioThreads = new ArrayList<>();
sockets = new ArrayList<>();
endpoints = new HashMap<>();
}
private void destroy() throws IOException
{
assert (sockets.isEmpty());
for (IOThread it : ioThreads) {
it.stop();
}
for (IOThread it : ioThreads) {
it.close();
}
ioThreads.clear();
selectorSync.lock();
try {
for (Selector selector : selectors) {
if (selector != null) {
selector.close();
}
}
selectors.clear();
}
finally {
selectorSync.unlock();
}
// Deallocate the reaper thread object.
if (reaper != null) {
reaper.close();
}
// Deallocate the array of mailboxes. No special work is
// needed as mailboxes themselves were deallocated with their
// corresponding io_thread/socket objects.
termMailbox.close();
active = false;
}
/**
* @return false if {@link #terminate()}terminate() has been called.
*/
public boolean isActive()
{
return active;
}
/**
* @return false if {@link #terminate()}terminate() has been called.
* @deprecated use {@link #isActive()} instead
*/
@Deprecated
public boolean checkTag()
{
return active;
}
// This function is called when user invokes zmq_term. If there are
// no more sockets open it'll cause all the infrastructure to be shut
// down. If there are open sockets still, the deallocation happens
// after the last one is closed.
public void terminate()
{
slotSync.lock();
try {
// Connect up any pending inproc connections, otherwise we will hang
for (Entry pending : pendingConnections.entries()) {
SocketBase s = createSocket(ZMQ.ZMQ_PAIR);
// create_socket might fail eg: out of memory/sockets limit reached
assert (s != null);
s.bind(pending.getValue());
s.close();
}
if (!starting.get()) {
// Check whether termination was already underway, but interrupted and now
// restarted.
boolean restarted = terminating;
terminating = true;
// First attempt to terminate the context.
if (!restarted) {
// First send stop command to sockets so that any blocking calls
// can be interrupted. If there are no sockets we can ask reaper
// thread to stop.
for (SocketBase socket : sockets) {
socket.stop();
}
if (sockets.isEmpty()) {
reaper.stop();
}
}
}
}
finally {
slotSync.unlock();
}
if (!starting.get()) {
// Wait till reaper thread closes all the sockets.
Command cmd = termMailbox.recv(WAIT_FOREVER);
if (cmd == null) {
throw new ZMQException(errno.get());
}
assert (cmd.type == Command.Type.DONE) : cmd;
slotSync.lock();
try {
assert (sockets.isEmpty());
}
finally {
slotSync.unlock();
}
}
// Deallocate the resources.
try {
destroy();
}
catch (IOException e) {
throw new ZError.IOException(e);
}
}
final void shutdown()
{
slotSync.lock();
try {
if (!starting.get() && !terminating) {
terminating = true;
// Send stop command to sockets so that any blocking calls
// can be interrupted. If there are no sockets we can ask reaper
// thread to stop.
for (SocketBase socket : sockets) {
socket.stop();
}
if (sockets.isEmpty()) {
reaper.stop();
}
}
}
finally {
slotSync.unlock();
}
}
private void chechStarted()
{
if (!starting.get()) {
throw new IllegalStateException("Already started");
}
}
/**
* Set the handler invoked when a {@link zmq.poll.Poller} abruptly terminates due to an uncaught exception.
* It defaults to the value of {@link Thread#getDefaultUncaughtExceptionHandler()}
* @param handler The object to use as this thread's uncaught exception handler. If null then this thread has no
* explicit handler and will use the one defined for the {@link ThreadGroup}.
* @throws IllegalStateException If context was already initialized by the creation of a socket
*/
public void setUncaughtExceptionHandler(UncaughtExceptionHandler handler)
{
chechStarted();
exhandler = handler;
}
/**
* @return The handler invoked when a {@link zmq.poll.Poller} abruptly terminates due to an uncaught exception.
*/
public UncaughtExceptionHandler getUncaughtExceptionHandler()
{
return exhandler;
}
/**
* In {@link zmq.poll.Poller#run()}, some non-fatal exceptions can be thrown. This handler will be notified, so they can
* be logged.
* Default to {@link Throwable#printStackTrace()}
* @param handler The object to use as this thread's handler for recoverable exceptions notifications.
* @throws IllegalStateException If context was already initialized by the creation of a socket
*/
public void setNotificationExceptionHandler(UncaughtExceptionHandler handler)
{
chechStarted();
exnotification = handler;
}
/**
* @return The handler invoked when a non-fatal exceptions is thrown in zmq.poll.Poller#run()
*/
public UncaughtExceptionHandler getNotificationExceptionHandler()
{
return exnotification;
}
/**
* Used to define a custom thread factory. It can be used to create thread that will be bounded to a CPU for
* performance or tweaks the created thread. It the UncaughtExceptionHandler is not set, the created thread UncaughtExceptionHandler
* will not be changed, so the factory can also be used to set it.
*
* @param threadFactory the thread factory used by {@link zmq.poll.Poller}
* @throws IllegalStateException If context was already initialized by the creation of a socket
*/
public void setThreadFactory(BiFunction threadFactory)
{
chechStarted();
this.threadFactory = threadFactory;
}
/**
* @return the current thread factory
*/
public BiFunction getThreadFactory()
{
return threadFactory;
}
/**
* Set an option
* @param option the option to set
* @param optval the option value
* @return true is the option is allowed for a context and the value is valid for the option
* @throws IllegalStateException If context was already initialized by the creation of a socket, and the
* option can't be changed.
*/
public boolean set(int option, int optval)
{
if (option == ZMQ.ZMQ_MAX_SOCKETS && optval >= 1) {
chechStarted();
optSync.lock();
try {
maxSockets = optval;
}
finally {
optSync.unlock();
}
}
else if (option == ZMQ.ZMQ_IO_THREADS && optval >= 0) {
chechStarted();
optSync.lock();
try {
ioThreadCount = optval;
}
finally {
optSync.unlock();
}
}
else if (option == ZMQ.ZMQ_BLOCKY && optval >= 0) {
optSync.lock();
try {
blocky = (optval != 0);
}
finally {
optSync.unlock();
}
}
else if (option == ZMQ.ZMQ_IPV6 && optval >= 0) {
optSync.lock();
try {
ipv6 = (optval != 0);
}
finally {
optSync.unlock();
}
}
else {
return false;
}
return true;
}
public int get(int option)
{
int rc;
if (option == ZMQ.ZMQ_MAX_SOCKETS) {
rc = maxSockets;
}
else if (option == ZMQ.ZMQ_IO_THREADS) {
rc = ioThreadCount;
}
else if (option == ZMQ.ZMQ_BLOCKY) {
rc = blocky ? 1 : 0;
}
else if (option == ZMQ.ZMQ_IPV6) {
rc = ipv6 ? 1 : 0;
}
else {
throw new IllegalArgumentException("option = " + option);
}
return rc;
}
public SocketBase createSocket(int type)
{
SocketBase s;
slotSync.lock();
try {
if (starting.compareAndSet(true, false)) {
initSlots();
}
// Once zmq_term() was called, we can't create new sockets.
if (terminating) {
throw new ZError.CtxTerminatedException();
}
// If maxSockets limit was reached, return error.
if (emptySlots.isEmpty()) {
throw new ZMQException(ZError.EMFILE);
}
// Choose a slot for the socket.
int slot = emptySlots.pollLast();
// Generate new unique socket ID.
int sid = maxSocketId.incrementAndGet();
// Create the socket and register its mailbox.
s = Sockets.create(type, this, slot, sid);
if (s == null) {
emptySlots.addLast(slot);
return null;
}
sockets.add(s);
slots[slot] = s.getMailbox();
}
finally {
slotSync.unlock();
}
return s;
}
private void initSlots()
{
slotSync.lock();
try {
// Initialize the array of mailboxes. Additional two slots are for
// zmq_term thread and reaper thread.
int ios;
optSync.lock();
try {
ios = ioThreadCount;
slotCount = maxSockets + ioThreadCount + 2;
}
finally {
optSync.unlock();
}
slots = new IMailbox[slotCount];
// Initialize the infrastructure for zmq_term thread.
slots[TERM_TID] = termMailbox;
// Create the reaper thread.
reaper = new Reaper(this, REAPER_TID);
slots[REAPER_TID] = reaper.getMailbox();
reaper.start();
// Create I/O thread objects and launch them.
for (int i = 2; i != ios + 2; i++) {
IOThread ioThread = new IOThread(this, i);
//alloc_assert (io_thread);
ioThreads.add(ioThread);
slots[i] = ioThread.getMailbox();
ioThread.start();
}
// In the unused part of the slot array, create a list of empty slots.
for (int i = slotCount - 1; i >= ios + 2; i--) {
emptySlots.add(i);
slots[i] = null;
}
}
finally {
slotSync.unlock();
}
}
void destroySocket(SocketBase socket)
{
slotSync.lock();
// Free the associated thread slot.
try {
int tid = socket.getTid();
emptySlots.add(tid);
slots[tid] = null;
// Remove the socket from the list of sockets.
sockets.remove(socket);
// If zmq_term() was already called and there are no more socket
// we can ask reaper thread to terminate.
if (terminating && sockets.isEmpty()) {
reaper.stop();
}
}
finally {
slotSync.unlock();
}
}
// Creates a Selector that will be closed when the context is destroyed.
public Selector createSelector()
{
selectorSync.lock();
try {
Selector selector = Selector.open();
assert (selector != null);
selectors.add(selector);
return selector;
}
catch (IOException e) {
throw new ZError.IOException(e);
}
finally {
selectorSync.unlock();
}
}
public boolean closeSelector(Selector selector)
{
selectorSync.lock();
try {
boolean rc = selectors.remove(selector);
if (rc) {
try {
selector.close();
}
catch (IOException e) {
throw new ZError.IOException(e);
}
}
return rc;
}
finally {
selectorSync.unlock();
}
}
// Returns reaper thread object.
ZObject getReaper()
{
return reaper;
}
// Send command to the destination thread.
void sendCommand(int tid, final Command command)
{
// System.out.println(Thread.currentThread().getName() + ": Sending command " + command);
slots[tid].send(command);
}
// Returns the I/O thread that is the least busy at the moment.
// Affinity specifies which I/O threads are eligible (0 = all).
// Returns NULL if no I/O thread is available.
IOThread chooseIoThread(long affinity)
{
if (ioThreads.isEmpty()) {
return null;
}
// Find the I/O thread with minimum load.
int minLoad = -1;
IOThread selectedIoThread = null;
for (int i = 0; i != ioThreads.size(); i++) {
if (affinity == 0 || (affinity & (1L << i)) > 0) {
int load = ioThreads.get(i).getLoad();
if (selectedIoThread == null || load < minLoad) {
minLoad = load;
selectedIoThread = ioThreads.get(i);
}
}
}
return selectedIoThread;
}
// Management of inproc endpoints.
boolean registerEndpoint(String addr, Endpoint endpoint)
{
endpointsSync.lock();
Endpoint inserted;
try {
inserted = endpoints.put(addr, endpoint);
}
finally {
endpointsSync.unlock();
}
return inserted == null;
}
boolean unregisterEndpoint(String addr, SocketBase socket)
{
endpointsSync.lock();
try {
Endpoint endpoint = endpoints.get(addr);
if (endpoint != null && socket == endpoint.socket) {
endpoints.remove(addr);
return true;
}
}
finally {
endpointsSync.unlock();
}
return false;
}
void unregisterEndpoints(SocketBase socket)
{
endpointsSync.lock();
try {
endpoints.entrySet().removeIf(e -> e.getValue().socket == socket);
}
finally {
endpointsSync.unlock();
}
}
Endpoint findEndpoint(String addr)
{
Endpoint endpoint;
endpointsSync.lock();
try {
endpoint = endpoints.get(addr);
if (endpoint == null) {
return new Endpoint(null, new Options());
}
// Increment the command sequence number of the peer so that it won't
// get deallocated until "bind" command is issued by the caller.
// The subsequent 'bind' has to be called with inc_seqnum parameter
// set to false, so that the seqnum isn't incremented twice.
endpoint.socket.incSeqnum();
}
finally {
endpointsSync.unlock();
}
return endpoint;
}
void pendConnection(String addr, Endpoint endpoint, Pipe[] pipes)
{
PendingConnection pendingConnection = new PendingConnection(endpoint, pipes[0], pipes[1]);
endpointsSync.lock();
try {
Endpoint existing = endpoints.get(addr);
if (existing == null) {
// Still no bind.
endpoint.socket.incSeqnum();
pendingConnections.insert(addr, pendingConnection);
}
else {
// Bind has happened in the mean time, connect directly
connectInprocSockets(existing.socket, existing.options, pendingConnection, Side.CONNECT);
}
}
finally {
endpointsSync.unlock();
}
}
void connectPending(String addr, SocketBase bindSocket)
{
endpointsSync.lock();
try {
Collection pendings = pendingConnections.remove(addr);
if (pendings != null) {
for (PendingConnection pending : pendings) {
connectInprocSockets(bindSocket, endpoints.get(addr).options, pending, Side.BIND);
}
}
}
finally {
endpointsSync.unlock();
}
}
private void connectInprocSockets(SocketBase bindSocket, Options bindOptions, PendingConnection pendingConnection,
Side side)
{
bindSocket.incSeqnum();
pendingConnection.bindPipe.setTid(bindSocket.getTid());
if (!bindOptions.recvIdentity) {
Msg msg = pendingConnection.bindPipe.read();
assert (msg != null);
}
int sndhwm = 0;
if (pendingConnection.endpoint.options.sendHwm != 0 && bindOptions.recvHwm != 0) {
sndhwm = pendingConnection.endpoint.options.sendHwm + bindOptions.recvHwm;
}
int rcvhwm = 0;
if (pendingConnection.endpoint.options.recvHwm != 0 && bindOptions.sendHwm != 0) {
rcvhwm = pendingConnection.endpoint.options.recvHwm + bindOptions.sendHwm;
}
boolean conflate = pendingConnection.endpoint.options.conflate
&& (pendingConnection.endpoint.options.type == ZMQ.ZMQ_DEALER
|| pendingConnection.endpoint.options.type == ZMQ.ZMQ_PULL
|| pendingConnection.endpoint.options.type == ZMQ.ZMQ_PUSH
|| pendingConnection.endpoint.options.type == ZMQ.ZMQ_PUB
|| pendingConnection.endpoint.options.type == ZMQ.ZMQ_SUB);
int[] hwms = { conflate ? -1 : sndhwm, conflate ? -1 : rcvhwm };
pendingConnection.connectPipe.setHwms(hwms[1], hwms[0]);
pendingConnection.bindPipe.setHwms(hwms[0], hwms[1]);
if (bindOptions.canReceiveDisconnectMsg && bindOptions.disconnectMsg != null) {
pendingConnection.connectPipe.setDisconnectMsg(bindOptions.disconnectMsg);
}
if (side == Side.BIND) {
Command cmd = new Command(null, Command.Type.BIND, pendingConnection.bindPipe);
bindSocket.processCommand(cmd);
bindSocket.sendInprocConnected(pendingConnection.endpoint.socket);
}
else {
pendingConnection.connectPipe.sendBind(bindSocket, pendingConnection.bindPipe, false);
}
// When a ctx is terminated all pending inproc connection will be
// connected, but the socket will already be closed and the pipe will be
// in waiting_for_delimiter state, which means no more writes can be done
// and the identity write fails and causes an assert. Check if the socket
// is open before sending.
if (pendingConnection.endpoint.options.recvIdentity && pendingConnection.endpoint.socket.isActive()) {
Msg id = new Msg(bindOptions.identitySize);
id.put(bindOptions.identity, 0, bindOptions.identitySize);
id.setFlags(Msg.IDENTITY);
boolean written = pendingConnection.bindPipe.write(id);
assert (written);
pendingConnection.bindPipe.flush();
}
// If set, send the hello msg of the peer to the local socket.
if (bindOptions.canSendHelloMsg && bindOptions.helloMsg != null) {
boolean written = pendingConnection.bindPipe.write(bindOptions.helloMsg);
assert (written);
pendingConnection.bindPipe.flush();
}
}
public Errno errno()
{
return errno;
}
/**
* Forward a channel in a monitor socket.
* @param channel a channel to forward
* @return the handle of the channel to be forwarded, used to retrieve it in {@link #getForwardedChannel(Integer)}
*/
int forwardChannel(SelectableChannel channel)
{
synchronized (ChannelForwardHolder.class) {
if (forwardHolder == null) {
forwardHolder = new ChannelForwardHolder();
}
}
WeakReference ref = new WeakReference<>(channel, forwardHolder.queue);
int handle = forwardHolder.handleSource.getAndIncrement();
forwardHolder.map.put(handle, ref);
forwardHolder.reversemap.put(ref, handle);
cleanForwarded();
return handle;
}
/**
* Retrieve a channel, using the handle returned by {@link #forwardChannel(SelectableChannel)}. As WeakReference are used, if the channel was discarded
* and a GC ran, it will not be found and this method will return null.
* @param handle
* @return
*/
SelectableChannel getForwardedChannel(Integer handle)
{
cleanForwarded();
WeakReference ref = forwardHolder.map.remove(handle);
if (ref != null) {
return ref.get();
}
else {
return null;
}
}
/**
* Clean all empty references
*/
private void cleanForwarded()
{
Reference extends SelectableChannel> ref;
while ((ref = forwardHolder.queue.poll()) != null) {
Integer handle = forwardHolder.reversemap.remove(ref);
forwardHolder.map.remove(handle);
}
}
private Thread createThread(Runnable target, String name)
{
Thread t = new Thread(target, name);
t.setDaemon(true);
t.setUncaughtExceptionHandler(getUncaughtExceptionHandler());
return t;
}
}
jeromq-0.6.0/src/main/java/zmq/IMailbox.java 0000664 0000000 0000000 00000000234 14557711263 0020636 0 ustar 00root root 0000000 0000000 package zmq;
import java.io.Closeable;
public interface IMailbox extends Closeable
{
void send(final Command cmd);
Command recv(long timeout);
}
jeromq-0.6.0/src/main/java/zmq/Mailbox.java 0000664 0000000 0000000 00000003707 14557711263 0020535 0 ustar 00root root 0000000 0000000 package zmq;
import java.io.IOException;
import java.nio.channels.SelectableChannel;
import java.util.Deque;
import java.util.concurrent.ConcurrentLinkedDeque;
import zmq.util.Errno;
public class Mailbox implements IMailbox
{
// The pipe to store actual commands.
private final Deque cpipe;
// Signaler to pass signals from writer thread to reader thread.
// kept it although a ConcurrentLinkedDeque, because the signaler channel is used in many places.
private final Signaler signaler;
// mailbox name, for better debugging
private final String name;
private final Errno errno;
public Mailbox(Ctx ctx, String name, int tid)
{
this.errno = ctx.errno();
cpipe = new ConcurrentLinkedDeque<>();
signaler = new Signaler(ctx, tid, errno);
this.name = name;
}
public SelectableChannel getFd()
{
return signaler.getFd();
}
@Override
public void send(final Command cmd)
{
cpipe.addLast(cmd);
signaler.send();
}
@Override
public Command recv(long timeout)
{
Command cmd = cpipe.pollFirst();
while (cmd == null) {
// Wait for signal from the command sender.
boolean rc = signaler.waitEvent(timeout);
if (!rc) {
assert (errno.get() == ZError.EAGAIN || errno.get() == ZError.EINTR) : errno.get();
break;
}
// Receive the signal.
signaler.recv();
if (errno.get() == ZError.EINTR) {
break;
}
// Get a command.
// Another thread may already fetch the command, so loop on it
cmd = cpipe.pollFirst();
}
return cmd;
}
@Override
public void close() throws IOException
{
signaler.close();
}
@Override
public String toString()
{
return super.toString() + "[" + name + "]";
}
}
jeromq-0.6.0/src/main/java/zmq/MailboxSafe.java 0000664 0000000 0000000 00000007056 14557711263 0021335 0 ustar 00root root 0000000 0000000 package zmq;
import zmq.pipe.YPipe;
import zmq.util.Errno;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;
@Deprecated
public class MailboxSafe implements IMailbox
{
// The pipe to store actual commands.
private final YPipe cpipe;
// Synchronize access to the mailbox from receivers and senders
private final ReentrantLock sync;
// Condition variable to pass signals from writer thread to reader thread.
private final Condition condition;
private final ArrayList signalers;
// mailbox name, for better debugging
private final String name;
private final Errno errno;
public MailboxSafe(Ctx ctx, ReentrantLock sync, String name)
{
this.errno = ctx.errno();
this.cpipe = new YPipe<>(Config.COMMAND_PIPE_GRANULARITY.getValue());
this.sync = sync;
this.condition = this.sync.newCondition();
this.signalers = new ArrayList<>(10);
this.name = name;
// Get the pipe into passive state. That way, if the users starts by
// polling on the associated file descriptor it will get woken up when
// new command is posted.
Command cmd = cpipe.read();
assert (cmd == null);
}
public void addSignaler(Signaler signaler)
{
this.signalers.add(signaler);
}
public void removeSignaler(Signaler signaler)
{
this.signalers.remove(signaler);
}
public void clearSignalers()
{
this.signalers.clear();
}
@Override
public void send(Command cmd)
{
sync.lock();
try {
cpipe.write(cmd, false);
boolean ok = cpipe.flush();
if (!ok) {
condition.signalAll();
for (Signaler signaler : signalers) {
signaler.send();
}
}
}
finally {
sync.unlock();
}
}
@Override
public Command recv(long timeout)
{
Command cmd;
// Try to get the command straight away.
cmd = cpipe.read();
if (cmd != null) {
return cmd;
}
// If the timeout is zero, it will be quicker to release the lock, giving other a chance to send a command
// and immediately relock it.
if (timeout == 0) {
sync.unlock();
sync.lock();
}
else {
try {
// Wait for signal from the command sender.
if (timeout == -1) {
condition.await();
}
else {
condition.await(timeout, TimeUnit.MILLISECONDS);
}
}
catch (InterruptedException e) {
// Restore interrupted state...
Thread.currentThread().interrupt();
errno.set(ZError.EINTR);
return null;
}
}
// Another thread may already fetch the command
cmd = cpipe.read();
if (cmd == null) {
errno.set(ZError.EAGAIN);
return null;
}
return cmd;
}
@Override
public void close()
{
// Work around problem that other threads might still be in our
// send() method, by waiting on the mutex before disappearing.
sync.lock();
sync.unlock();
}
@Override
public String toString()
{
return super.toString() + "[" + name + "]";
}
}
jeromq-0.6.0/src/main/java/zmq/Msg.java 0000664 0000000 0000000 00000027434 14557711263 0017673 0 ustar 00root root 0000000 0000000 package zmq;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.SocketChannel;
import java.util.Arrays;
import zmq.io.Metadata;
import zmq.util.Utils;
import zmq.util.Wire;
public class Msg
{
// dynamic message building used when the size is not known in advance
public static final class Builder extends Msg
{
private final ByteArrayOutputStream out = new ByteArrayOutputStream();
public Builder()
{
super();
}
@Override
public int size()
{
return out.size();
}
@Override
protected Msg put(int index, byte b)
{
out.write(b);
return this;
}
@Override
public Msg put(byte[] src, int off, int len)
{
if (src == null) {
return this;
}
out.write(src, off, len);
setWriteIndex(getWriteIndex() + len);
return this;
}
@Override
public Msg put(ByteBuffer src, int off, int len)
{
if (src == null) {
return this;
}
for (int idx = off; idx < off + len; ++idx) {
out.write(src.get(idx));
}
setWriteIndex(getWriteIndex() + len);
return this;
}
@Override
public Msg putShortString(String data)
{
if (data == null) {
return this;
}
int length = data.length();
Utils.checkArgument(length < 256, "String must be strictly smaller than 256 characters");
out.write((byte) length);
out.write(data.getBytes(ZMQ.CHARSET), 0, length);
setWriteIndex(getWriteIndex() + length + 1);
return this;
}
@Override
public void setFlags(int flags)
{
super.setFlags(flags);
}
public Msg build()
{
return new Msg(this, out);
}
}
enum Type
{
DATA,
JOIN,
LEAVE,
DELIMITER
}
public static final int MORE = 1; // Followed by more parts
public static final int COMMAND = 2; // Command frame (see ZMTP spec)
public static final int CREDENTIAL = 32;
public static final int IDENTITY = 64;
public static final int SHARED = 128;
/// The maximum length of a group (Radio/Dish)
public static final int MAX_GROUP_LENGTH = 255;
private Metadata metadata;
private int flags;
private Type type;
// the file descriptor where this message originated, needs to be 64bit due to alignment
private SocketChannel fileDesc;
private final int size;
private final ByteBuffer buf;
// keep track of relative write position
private int writeIndex = 0;
// keep track of relative read position
private int readIndex = 0;
private int routingId;
private String group;
public Msg()
{
this(0);
}
public Msg(int capacity)
{
this.type = Type.DATA;
this.flags = 0;
this.size = capacity;
this.buf = ByteBuffer.wrap(new byte[capacity]).order(ByteOrder.BIG_ENDIAN);
}
public Msg(byte[] src)
{
if (src == null) {
src = new byte[0];
}
this.type = Type.DATA;
this.flags = 0;
this.size = src.length;
this.buf = ByteBuffer.wrap(src).order(ByteOrder.BIG_ENDIAN);
}
public Msg(final ByteBuffer src)
{
if (src == null) {
throw new IllegalArgumentException("ByteBuffer cannot be null");
}
this.type = Type.DATA;
this.flags = 0;
this.buf = src.duplicate();
this.size = buf.remaining();
}
public Msg(final Msg m)
{
if (m == null) {
throw new IllegalArgumentException("Msg cannot be null");
}
this.type = m.type;
this.flags = m.flags;
this.size = m.size;
this.buf = m.buf != null ? m.buf.duplicate() : null;
}
private Msg(Msg src, ByteArrayOutputStream out)
{
this(ByteBuffer.wrap(out.toByteArray()));
this.type = src.type;
this.flags = src.flags;
}
public boolean isIdentity()
{
return (flags & IDENTITY) == IDENTITY;
}
public boolean isDelimiter()
{
return type == Type.DELIMITER;
}
public boolean isJoin()
{
return type == Type.JOIN;
}
public boolean isLeave()
{
return type == Type.LEAVE;
}
public boolean check()
{
return true; // type >= TYPE_MIN && type <= TYPE_MAX;
}
public int flags()
{
return flags;
}
public boolean hasMore()
{
return (flags & MORE) > 0;
}
public boolean isCommand()
{
return (flags & COMMAND) == COMMAND;
}
public boolean isCredential()
{
return (flags & CREDENTIAL) == CREDENTIAL;
}
public void setFlags(int flags)
{
this.flags |= flags;
}
public void initDelimiter()
{
type = Type.DELIMITER;
metadata = null;
flags = 0;
}
public void initJoin()
{
type = Type.JOIN;
metadata = null;
flags = 0;
}
public void initLeave()
{
type = Type.LEAVE;
metadata = null;
flags = 0;
}
/**
* Returns the message data.
*
* If possible, a reference to the data is returned, without copy.
* Otherwise a new byte array will be allocated and the data will be copied.
*
* @return the message data.
*/
public byte[] data()
{
if (buf.hasArray()) {
byte[] array = buf.array();
int offset = buf.arrayOffset();
if (offset == 0 && array.length == size) {
// If the backing array is exactly what we need, return it without copy.
return array;
}
else {
// Else use it to make an efficient copy.
return Arrays.copyOfRange(array, offset, offset + size);
}
}
else {
// No backing array -> use ByteBuffer#get().
byte[] array = new byte[size];
ByteBuffer dup = buf.duplicate();
dup.position(0);
dup.get(array);
return array;
}
}
public ByteBuffer buf()
{
return buf.duplicate();
}
public int size()
{
return size;
}
public void resetFlags(int f)
{
flags = flags & ~f;
}
public void setFd(SocketChannel fileDesc)
{
this.fileDesc = fileDesc;
}
// TODO V4 use the source channel
public SocketChannel fd()
{
return fileDesc;
}
public Metadata getMetadata()
{
return metadata;
}
public Msg setMetadata(Metadata metadata)
{
this.metadata = metadata;
return this;
}
public void resetMetadata()
{
setMetadata(null);
}
public byte get()
{
return get(readIndex++);
}
public byte get(int index)
{
return buf.get(index);
}
public Msg put(byte b)
{
return put(writeIndex++, b);
}
public Msg put(int b)
{
return put(writeIndex++, (byte) b);
}
protected Msg put(int index, byte b)
{
buf.put(index, b);
return this;
}
public Msg put(byte[] src)
{
return put(src, 0, src.length);
}
public Msg put(byte[] src, int off, int len)
{
if (src == null) {
return this;
}
ByteBuffer dup = buf.duplicate();
dup.position(writeIndex);
writeIndex += len;
dup.put(src, off, len);
return this;
}
public Msg put(ByteBuffer src, int off, int len)
{
if (src == null) {
return this;
}
int position = src.position();
int limit = src.limit();
src.limit(off + len).position(off);
put(src);
src.limit(limit).position(position);
return this;
}
public Msg put(ByteBuffer src)
{
ByteBuffer dup = buf.duplicate();
dup.position(writeIndex);
writeIndex += Math.min(dup.remaining(), src.remaining());
dup.put(src);
return this;
}
public int getBytes(int index, byte[] dst, int off, int len)
{
int count = Math.min(len, size - index);
if (buf.hasArray()) {
System.arraycopy(buf.array(), buf.arrayOffset() + index, dst, off, count);
}
else {
ByteBuffer dup = buf.duplicate();
dup.position(index);
dup.get(dst, off, count);
}
return count;
}
public int getBytes(int index, ByteBuffer bb, int len)
{
ByteBuffer dup = buf.duplicate();
dup.position(index);
int count = Math.min(bb.remaining(), dup.remaining());
count = Math.min(count, len);
bb.put(dup);
return count;
}
@Override
public String toString()
{
return String.format("#zmq.Msg{type=%s, size=%s, flags=%s}", type, size, flags);
}
protected final int getWriteIndex()
{
return writeIndex;
}
protected final void setWriteIndex(int writeIndex)
{
this.writeIndex = writeIndex;
}
public long getLong(int offset)
{
return Wire.getUInt64(buf, offset);
}
public int getInt(int offset)
{
return Wire.getUInt32(buf, offset);
}
public int getShort(int offset)
{
return Wire.getUInt16(buf, offset);
}
public void transfer(ByteBuffer destination, int srcOffset, int srcLength)
{
int position = buf.position();
int limit = buf.limit();
buf.limit(srcOffset + srcLength).position(srcOffset);
destination.put(buf);
buf.limit(limit).position(position);
}
/**
* Puts a string into the message, prefixed with its length.
* Users shall size the message by adding 1 to the length of the string:
* It needs to be able to accommodate (data.length+1) more bytes.
*
* @param data a string shorter than 256 characters. If null, defaults to a no-op.
* @return the same message.
*/
public Msg putShortString(String data)
{
if (data == null) {
return this;
}
ByteBuffer dup = buf.duplicate();
dup.position(writeIndex);
writeIndex += Wire.putShortString(dup, data);
return this;
}
/**
* Return the routing id of a message. The routing id represent the CLIENT socket that sent the message to the
* SERVER socket.
* @return the routing id
* */
public int getRoutingId()
{
return routingId;
}
/**
* Set the routing id on a message. The routing id represent the CLIENT socket which the message should be sent to.
* Only SERVER socket is currently using the routing id.
* @param routingId the routing id
* @return true if successfully set the routing id.
*/
public boolean setRoutingId(int routingId)
{
if (routingId != 0) {
this.routingId = routingId;
return true;
}
return false;
}
/**
* Retrieve the group for RADIO/DISH sockets
* @return the group.
*/
public String getGroup()
{
return group;
}
/**
* Set the group for RADIO/DISH sockets
* @param group
* @return true if successfully set the group.
*/
public boolean setGroup(String group)
{
if (group.length() > MAX_GROUP_LENGTH) {
return false;
}
this.group = group;
return true;
}
public void resetRoutingId()
{
routingId = 0;
}
}
jeromq-0.6.0/src/main/java/zmq/Options.java 0000664 0000000 0000000 00000067114 14557711263 0020577 0 ustar 00root root 0000000 0000000 package zmq;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import zmq.io.coder.IDecoder;
import zmq.io.coder.IEncoder;
import zmq.io.mechanism.Mechanisms;
import zmq.io.net.SelectorProviderChooser;
import zmq.io.net.ipc.IpcAddress;
import zmq.io.net.tcp.TcpAddress;
import zmq.io.net.tcp.TcpAddress.TcpAddressMask;
import zmq.msg.MsgAllocator;
import zmq.msg.MsgAllocatorThreshold;
import zmq.util.Errno;
import zmq.util.ValueReference;
import zmq.util.Z85;
public class Options
{
// High-water marks for message pipes.
public int sendHwm = ZMQ.DEFAULT_SEND_HWM;
public int recvHwm = ZMQ.DEFAULT_RECV_HWM;
// I/O thread affinity.
public long affinity = ZMQ.DEFAULT_AFFINITY;
// Socket identity
public short identitySize = (short) ZMQ.DEFAULT_IDENTITY.length;
public byte[] identity = ZMQ.DEFAULT_IDENTITY;
// Maximum tranfer rate [kb/s]. Default 100kb/s.
int rate = ZMQ.DEFAULT_RATE;
// Reliability time interval [ms]. Default 10 seconds.
int recoveryIvl = ZMQ.DEFAULT_RECOVERY_IVL;
// Sets the time-to-live field in every multicast packet sent.
int multicastHops = ZMQ.DEFAULT_MULTICAST_HOPS;
// SO_SNDBUF and SO_RCVBUF to be passed to underlying transport sockets.
public int sndbuf = ZMQ.DEFAULT_SNDBUF;
public int rcvbuf = ZMQ.DEFAULT_RCVBUF;
// Type of service (containing DSCP and ECN socket options)
public int tos = ZMQ.DEFAULT_TOS;
// Socket type.
public int type = ZMQ.DEFAULT_TYPE;
// Linger time, in milliseconds.
public int linger = ZMQ.DEFAULT_LINGER;
// Minimum interval between attempts to reconnect, in milliseconds.
// Default 100ms
public int reconnectIvl = ZMQ.DEFAULT_RECONNECT_IVL;
// Maximum interval between attempts to reconnect, in milliseconds.
// Default 0 (unused)
public int reconnectIvlMax = ZMQ.DEFAULT_RECONNECT_IVL_MAX;
// Maximum backlog for pending connections.
public int backlog = ZMQ.DEFAULT_BACKLOG;
// Maximal size of message to handle.
public long maxMsgSize = ZMQ.DEFAULT_MAX_MSG_SIZE;
// The timeout for send/recv operations for this socket.
int recvTimeout = ZMQ.DEFAULT_RECV_TIMEOUT;
int sendTimeout = ZMQ.DEFAULT_SEND_TIMEOUT;
// If true, IPv6 is enabled (as well as IPv4)
public boolean ipv6 = ZMQ.DEFAULT_IPV6;
// If false, connecting pipes are not attached immediately, meaning a send()
// on a socket with only connecting pipes would block
public boolean immediate = ZMQ.DEFAULT_IMMEDIATE;
// Addres of SOCKS proxy
public String socksProxyAddress = ZMQ.DEFAULT_SOCKS_PROXY_ADDRESS;
// TCP keep-alive settings.
// Defaults to -1 = do not change socket options
public int tcpKeepAlive = ZMQ.DEFAULT_TCP_KEEP_ALIVE;
public int tcpKeepAliveCnt = ZMQ.DEFAULT_TCP_KEEP_ALIVE_CNT;
public int tcpKeepAliveIdle = ZMQ.DEFAULT_TCP_KEEP_ALIVE_IDLE;
public int tcpKeepAliveIntvl = ZMQ.DEFAULT_TCP_KEEP_ALIVE_INTVL;
// Security mechanism for all connections on this socket
public Mechanisms mechanism = ZMQ.DEFAULT_MECHANISM;
// If peer is acting as server for PLAIN or CURVE mechanisms
public boolean asServer = ZMQ.DEFAULT_AS_SERVER;
// ZAP authentication domain
public String zapDomain = ZMQ.DEFAULT_ZAP_DOMAIN;
// Security credentials for PLAIN mechanism
public String plainUsername = null;
public String plainPassword = null;
// Security credentials for CURVE mechanism
// Normal base 256 key is 32 bytes
public static final int CURVE_KEYSIZE = 32;
// Key encoded using Z85 is 40 bytes
public static final int CURVE_KEYSIZE_Z85 = 40;
// No default, as an array can't really be a static final
public byte[] curvePublicKey = new byte[CURVE_KEYSIZE];
public byte[] curveSecretKey = new byte[CURVE_KEYSIZE];
public byte[] curveServerKey = new byte[CURVE_KEYSIZE];
// Principals for GSSAPI mechanism
String gssPrincipal = null;
String gssServicePrincipal = null;
// If true, gss encryption will be disabled
boolean gssPlaintext = ZMQ.DEFAULT_GSS_PLAINTEXT;
// If true, socket conflates outgoing/incoming messages.
// Applicable to dealer, push/pull, pub/sub socket types.
// Cannot receive multi-part messages.
// Ignores hwm
public boolean conflate = ZMQ.DEFAULT_CONFLATE;
// If connection handshake is not done after this many milliseconds,
// close socket. Default is 30 secs. 0 means no handshake timeout.
public int handshakeIvl = ZMQ.DEFAULT_HANDSHAKE_IVL;
// If remote peer receives a PING message and doesn't receive another
// message within the ttl value, it should close the connection
// (measured in tenths of a second)
public int heartbeatTtl = ZMQ.DEFAULT_HEARTBEAT_TTL;
// Time in milliseconds between sending heartbeat PING messages.
public int heartbeatInterval = ZMQ.DEFAULT_HEARTBEAT_INTERVAL;
// Time in milliseconds to wait for a PING response before disconnecting
public int heartbeatTimeout = ZMQ.DEFAULT_HEARTBEAT_TIMEOUT;
// the ping context that will be sent with each ping message.
public byte[] heartbeatContext = ZMQ.DEFAULT_HEARTBEAT_CONTEXT;
// threshold to allocate byte buffers on direct memory instead of heap.
// Set to <= 0 to disable this system.
public MsgAllocator allocator = ZMQ.DEFAULT_MSG_ALLOCATOR;
public SelectorProviderChooser selectorChooser = ZMQ.DEFAULT_SELECTOR_CHOOSER;
// Hello msg to send to peer upon connecting
public Msg helloMsg = ZMQ.DEFAULT_HELLO_MSG;
public boolean canSendHelloMsg = false;
// Disconnect message to receive when peer disconnect
public Msg disconnectMsg = ZMQ.DEFAULT_DISCONNECT_MSG;
public boolean canReceiveDisconnectMsg = false;
// Hiccup message to receive when the connecting peer experience an hiccup in the connection
public Msg hiccupMsg = ZMQ.DEFAULT_HICCUP_MSG;
public boolean canReceiveHiccupMsg = false;
// As Socket type on the network.
public int asType = ZMQ.DEFAULT_AS_TYPE;
// A metadata record name where the self address will be stored if defined
public String selfAddressPropertyName = ZMQ.DEFAULT_SELF_ADDRESS_PROPERTY_NAME;
// Last socket endpoint resolved URI
String lastEndpoint = null;
public final Errno errno = new Errno();
// TCP accept() filters
public final List tcpAcceptFilters = new ArrayList<>();
// IPC accept() filters
final List ipcAcceptFilters = new ArrayList<>();
// Last connected routing id for PEER socket
public int peerLastRoutingId = 0;
// ID of the socket.
public int socketId = 0;
// If 1, (X)SUB socket should filter the messages. If 0, it should not.
public boolean filter = false;
// If true, the identity message is forwarded to the socket.
public boolean recvIdentity = false;
// if true, router socket accepts non-zmq tcp connections
public boolean rawSocket = false;
public Class extends IDecoder> decoder = null;
public Class extends IEncoder> encoder = null;
@SuppressWarnings("deprecation")
public boolean setSocketOpt(int option, Object optval)
{
final ValueReference result = new ValueReference<>(false);
switch (option) {
case ZMQ.ZMQ_SNDHWM:
sendHwm = (Integer) optval;
if (sendHwm < 0) {
throw new IllegalArgumentException("sendHwm " + optval);
}
return true;
case ZMQ.ZMQ_RCVHWM:
recvHwm = (Integer) optval;
if (recvHwm < 0) {
throw new IllegalArgumentException("recvHwm " + optval);
}
return true;
case ZMQ.ZMQ_AFFINITY:
affinity = (Long) optval;
return true;
case ZMQ.ZMQ_IDENTITY:
byte[] val = parseBytes(option, optval);
if (val == null || val.length > 255) {
throw new IllegalArgumentException("identity must not be null or less than 255 " + optval);
}
identity = Arrays.copyOf(val, val.length);
identitySize = (short) identity.length;
return true;
case ZMQ.ZMQ_RATE:
rate = (Integer) optval;
return true;
case ZMQ.ZMQ_RECOVERY_IVL:
recoveryIvl = (Integer) optval;
return true;
case ZMQ.ZMQ_SNDBUF:
sndbuf = (Integer) optval;
return true;
case ZMQ.ZMQ_RCVBUF:
rcvbuf = (Integer) optval;
return true;
case ZMQ.ZMQ_TOS:
tos = (Integer) optval;
return true;
case ZMQ.ZMQ_LINGER:
linger = (Integer) optval;
return true;
case ZMQ.ZMQ_RECONNECT_IVL:
reconnectIvl = (Integer) optval;
if (reconnectIvl < -1) {
throw new IllegalArgumentException("reconnectIvl " + optval);
}
return true;
case ZMQ.ZMQ_RECONNECT_IVL_MAX:
reconnectIvlMax = (Integer) optval;
if (reconnectIvlMax < 0) {
throw new IllegalArgumentException("reconnectIvlMax " + optval);
}
return true;
case ZMQ.ZMQ_BACKLOG:
backlog = (Integer) optval;
return true;
case ZMQ.ZMQ_MAXMSGSIZE:
maxMsgSize = (Long) optval;
return true;
case ZMQ.ZMQ_MULTICAST_HOPS:
multicastHops = (Integer) optval;
return true;
case ZMQ.ZMQ_RCVTIMEO:
recvTimeout = (Integer) optval;
return true;
case ZMQ.ZMQ_SNDTIMEO:
sendTimeout = (Integer) optval;
return true;
/* Deprecated in favor of ZMQ_IPV6 */
case ZMQ.ZMQ_IPV4ONLY:
return setSocketOpt(ZMQ.ZMQ_IPV6, !parseBoolean(option, optval));
/* To replace the somewhat surprising IPV4ONLY */
case ZMQ.ZMQ_IPV6:
ipv6 = parseBoolean(option, optval);
return true;
case ZMQ.ZMQ_SOCKS_PROXY:
socksProxyAddress = parseString(option, optval);
return true;
case ZMQ.ZMQ_TCP_KEEPALIVE:
tcpKeepAlive = ((Number) optval).intValue();
if (tcpKeepAlive != -1 && tcpKeepAlive != 0 && tcpKeepAlive != 1) {
throw new IllegalArgumentException("tcpKeepAlive only accepts one of -1,0,1 " + optval);
}
return true;
case ZMQ.ZMQ_TCP_KEEPALIVE_CNT:
this.tcpKeepAliveCnt = ((Number) optval).intValue();
return true;
case ZMQ.ZMQ_TCP_KEEPALIVE_IDLE:
this.tcpKeepAliveIdle = ((Number) optval).intValue();
return true;
case ZMQ.ZMQ_TCP_KEEPALIVE_INTVL:
this.tcpKeepAliveIntvl = ((Number) optval).intValue();
return true;
case ZMQ.ZMQ_IMMEDIATE:
immediate = parseBoolean(option, optval);
return true;
case ZMQ.ZMQ_DELAY_ATTACH_ON_CONNECT:
immediate = !parseBoolean(option, optval);
return true;
case ZMQ.ZMQ_TCP_ACCEPT_FILTER:
String filterStr = parseString(option, optval);
if (filterStr == null) {
tcpAcceptFilters.clear();
}
else if (filterStr.isEmpty() || filterStr.length() > 255) {
throw new IllegalArgumentException("tcp_accept_filter " + optval);
}
else {
TcpAddressMask filter = new TcpAddressMask(filterStr, ipv6);
tcpAcceptFilters.add(filter);
}
return true;
case ZMQ.ZMQ_PLAIN_SERVER:
asServer = parseBoolean(option, optval);
mechanism = (asServer ? Mechanisms.PLAIN : Mechanisms.NULL);
return true;
case ZMQ.ZMQ_PLAIN_USERNAME:
if (optval == null) {
mechanism = Mechanisms.NULL;
asServer = false;
return true;
}
plainUsername = parseString(option, optval);
asServer = false;
mechanism = Mechanisms.PLAIN;
return true;
case ZMQ.ZMQ_PLAIN_PASSWORD:
if (optval == null) {
mechanism = Mechanisms.NULL;
asServer = false;
return true;
}
plainPassword = parseString(option, optval);
asServer = false;
mechanism = Mechanisms.PLAIN;
return true;
case ZMQ.ZMQ_ZAP_DOMAIN:
String domain = parseString(option, optval);
if (domain != null && domain.length() < 256) {
zapDomain = domain;
return true;
}
throw new IllegalArgumentException("zap domain length shall be < 256 : " + optval);
case ZMQ.ZMQ_CURVE_SERVER:
asServer = parseBoolean(option, optval);
mechanism = (asServer ? Mechanisms.CURVE : Mechanisms.NULL);
return true;
case ZMQ.ZMQ_CURVE_PUBLICKEY:
curvePublicKey = setCurveKey(option, optval, result);
return result.get();
case ZMQ.ZMQ_CURVE_SECRETKEY:
curveSecretKey = setCurveKey(option, optval, result);
return result.get();
case ZMQ.ZMQ_CURVE_SERVERKEY:
curveServerKey = setCurveKey(option, optval, result);
if (curveServerKey == null) {
asServer = false;
}
return result.get();
case ZMQ.ZMQ_CONFLATE:
conflate = parseBoolean(option, optval);
return true;
case ZMQ.ZMQ_GSSAPI_SERVER:
asServer = parseBoolean(option, optval);
mechanism = Mechanisms.GSSAPI;
return true;
case ZMQ.ZMQ_GSSAPI_PRINCIPAL:
gssPrincipal = parseString(option, optval);
mechanism = Mechanisms.GSSAPI;
return true;
case ZMQ.ZMQ_GSSAPI_SERVICE_PRINCIPAL:
gssServicePrincipal = parseString(option, optval);
mechanism = Mechanisms.GSSAPI;
return true;
case ZMQ.ZMQ_GSSAPI_PLAINTEXT:
gssPlaintext = parseBoolean(option, optval);
return true;
case ZMQ.ZMQ_HANDSHAKE_IVL:
handshakeIvl = (Integer) optval;
if (handshakeIvl < 0) {
throw new IllegalArgumentException("handshakeIvl only accept positive values " + optval);
}
return true;
case ZMQ.ZMQ_HEARTBEAT_IVL:
heartbeatInterval = (Integer) optval;
if (heartbeatInterval < 0) {
throw new IllegalArgumentException("heartbeatInterval only accept positive values " + optval);
}
return true;
case ZMQ.ZMQ_HEARTBEAT_TIMEOUT:
heartbeatTimeout = (Integer) optval;
if (heartbeatTimeout < 0) {
throw new IllegalArgumentException("heartbeatTimeout only accept positive values " + optval);
}
return true;
case ZMQ.ZMQ_HEARTBEAT_TTL:
Integer value = (Integer) optval;
// Convert this to deciseconds from milliseconds
value /= 100;
if (value >= 0 && value <= 6553) {
heartbeatTtl = value;
}
else {
throw new IllegalArgumentException("heartbeatTtl is out of range [0..655399]" + optval);
}
return true;
case ZMQ.ZMQ_HEARTBEAT_CONTEXT:
heartbeatContext = (byte[]) optval;
if (heartbeatContext == null) {
throw new IllegalArgumentException("heartbeatContext cannot be null");
}
return true;
case ZMQ.ZMQ_DECODER:
decoder = checkCustomCodec(optval, IDecoder.class);
rawSocket = true;
// failure throws ZError.InstantiationException
// if that line is reached, everything is fine
return true;
case ZMQ.ZMQ_ENCODER:
encoder = checkCustomCodec(optval, IEncoder.class);
rawSocket = true;
// failure throws ZError.InstantiationException
// if that line is reached, everything is fine
return true;
case ZMQ.ZMQ_MSG_ALLOCATOR:
if (optval instanceof String) {
try {
allocator = allocator(Class.forName((String) optval));
return true;
}
catch (ClassNotFoundException e) {
throw new IllegalArgumentException(e);
}
}
else if (optval instanceof Class) {
allocator = allocator((Class>) optval);
return true;
}
else if (optval instanceof MsgAllocator) {
allocator = (MsgAllocator) optval;
return true;
}
return false;
case ZMQ.ZMQ_MSG_ALLOCATION_HEAP_THRESHOLD:
Integer allocationHeapThreshold = (Integer) optval;
allocator = new MsgAllocatorThreshold(allocationHeapThreshold);
return true;
case ZMQ.ZMQ_SELECTOR_PROVIDERCHOOSER:
if (optval instanceof String) {
try {
selectorChooser = chooser(Class.forName((String) optval));
return true;
}
catch (ClassNotFoundException e) {
throw new IllegalArgumentException(e);
}
}
else if (optval instanceof Class) {
selectorChooser = chooser((Class>) optval);
return true;
}
else if (optval instanceof SelectorProviderChooser) {
selectorChooser = (SelectorProviderChooser) optval;
return true;
}
return false;
case ZMQ.ZMQ_HELLO_MSG:
if (optval == null) {
helloMsg = null;
}
else {
byte[] bytes = parseBytes(option, optval);
if (bytes.length == 0) {
helloMsg = null;
}
else {
helloMsg = new Msg(Arrays.copyOf(bytes, bytes.length));
}
}
return true;
case ZMQ.ZMQ_DISCONNECT_MSG:
if (optval == null) {
disconnectMsg = null;
}
else {
byte[] bytes = parseBytes(option, optval);
if (bytes.length == 0) {
disconnectMsg = null;
}
else {
disconnectMsg = new Msg(Arrays.copyOf(bytes, bytes.length));
}
}
return true;
case ZMQ.ZMQ_HICCUP_MSG:
if (optval == null) {
hiccupMsg = null;
}
else {
byte[] bytes = parseBytes(option, optval);
if (bytes.length == 0) {
hiccupMsg = null;
}
else {
hiccupMsg = new Msg(Arrays.copyOf(bytes, bytes.length));
}
}
return true;
case ZMQ.ZMQ_AS_TYPE:
this.asType = (Integer) optval;
return true;
case ZMQ.ZMQ_SELFADDR_PROPERTY_NAME:
this.selfAddressPropertyName = parseString(option, optval);
return true;
default:
throw new IllegalArgumentException("Unknown Option " + option);
}
}
private MsgAllocator allocator(Class> clazz)
{
try {
Class extends MsgAllocator> msgAllocator = clazz.asSubclass(MsgAllocator.class);
return msgAllocator.newInstance();
}
catch (InstantiationException | IllegalAccessException e) {
throw new IllegalArgumentException(e);
}
}
private SelectorProviderChooser chooser(Class> clazz)
{
try {
Class extends SelectorProviderChooser> selectorClazz = clazz.asSubclass(SelectorProviderChooser.class);
return selectorClazz.newInstance();
}
catch (ClassCastException e) {
throw new IllegalArgumentException(e);
}
catch (InstantiationException | IllegalAccessException e) {
throw new ZError.InstantiationException(e);
}
}
private Class extends T> checkCustomCodec(Object optval, Class type)
{
Class> clazz = (Class>) optval;
if (! type.isAssignableFrom(clazz)) {
throw new ZError.InstantiationException("Custom " + clazz.getCanonicalName() + " is not assignable from " + type.getCanonicalName());
}
Class extends T> custom = clazz.asSubclass(type);
try {
custom.getConstructor(int.class, long.class);
return custom;
}
catch (NoSuchMethodException | SecurityException e) {
String message = "Custom " + clazz.getCanonicalName()
+ " has no required constructor (int bufferSize, long maxMsgSize)";
throw new ZError.InstantiationException(message, e);
}
}
private byte[] setCurveKey(int option, Object optval, ValueReference result)
{
if (optval == null) {
// TODO V4 setting a curve key as null does change the mechanism type ?
result.set(false);
return null;
}
else {
byte[] key = null;
// if the optval is already the key don't do any parsing
if (optval instanceof byte[] && ((byte[]) optval).length == CURVE_KEYSIZE) {
key = (byte[]) optval;
result.set(true);
errno.set(0);
}
else {
String val = parseString(option, optval);
int length = val.length();
if (length == CURVE_KEYSIZE_Z85) {
key = Z85.decode(val);
result.set(true);
errno.set(0);
}
else if (length == CURVE_KEYSIZE) {
key = val.getBytes(ZMQ.CHARSET);
result.set(true);
errno.set(0);
}
else {
result.set(false);
errno.set(ZError.EINVAL);
}
}
if (key != null) {
mechanism = Mechanisms.CURVE;
}
return key;
}
}
@SuppressWarnings("deprecation")
public Object getSocketOpt(int option)
{
switch (option) {
case ZMQ.ZMQ_SNDHWM:
return sendHwm;
case ZMQ.ZMQ_RCVHWM:
return recvHwm;
case ZMQ.ZMQ_AFFINITY:
return affinity;
case ZMQ.ZMQ_IDENTITY:
return identity;
case ZMQ.ZMQ_RATE:
return rate;
case ZMQ.ZMQ_RECOVERY_IVL:
return recoveryIvl;
case ZMQ.ZMQ_SNDBUF:
return sndbuf;
case ZMQ.ZMQ_RCVBUF:
return rcvbuf;
case ZMQ.ZMQ_TOS:
return tos;
case ZMQ.ZMQ_TYPE:
return type;
case ZMQ.ZMQ_LINGER:
return linger;
case ZMQ.ZMQ_RECONNECT_IVL:
return reconnectIvl;
case ZMQ.ZMQ_RECONNECT_IVL_MAX:
return reconnectIvlMax;
case ZMQ.ZMQ_BACKLOG:
return backlog;
case ZMQ.ZMQ_MAXMSGSIZE:
return maxMsgSize;
case ZMQ.ZMQ_MULTICAST_HOPS:
return multicastHops;
case ZMQ.ZMQ_RCVTIMEO:
return recvTimeout;
case ZMQ.ZMQ_SNDTIMEO:
return sendTimeout;
case ZMQ.ZMQ_IPV4ONLY:
return !ipv6;
case ZMQ.ZMQ_IPV6:
return ipv6;
case ZMQ.ZMQ_TCP_KEEPALIVE:
return tcpKeepAlive;
case ZMQ.ZMQ_IMMEDIATE:
return immediate;
case ZMQ.ZMQ_DELAY_ATTACH_ON_CONNECT:
return !immediate;
case ZMQ.ZMQ_SOCKS_PROXY:
return socksProxyAddress;
case ZMQ.ZMQ_TCP_KEEPALIVE_CNT:
return tcpKeepAliveCnt;
case ZMQ.ZMQ_TCP_KEEPALIVE_IDLE:
return tcpKeepAliveIdle;
case ZMQ.ZMQ_TCP_KEEPALIVE_INTVL:
return tcpKeepAliveIntvl;
case ZMQ.ZMQ_MECHANISM:
return mechanism;
case ZMQ.ZMQ_PLAIN_SERVER:
return asServer && mechanism == Mechanisms.PLAIN;
case ZMQ.ZMQ_PLAIN_USERNAME:
return plainUsername;
case ZMQ.ZMQ_PLAIN_PASSWORD:
return plainPassword;
case ZMQ.ZMQ_ZAP_DOMAIN:
return zapDomain;
case ZMQ.ZMQ_LAST_ENDPOINT:
return lastEndpoint;
case ZMQ.ZMQ_CURVE_SERVER:
return asServer && mechanism == Mechanisms.CURVE;
case ZMQ.ZMQ_CURVE_PUBLICKEY:
return curvePublicKey;
case ZMQ.ZMQ_CURVE_SERVERKEY:
return curveServerKey;
case ZMQ.ZMQ_CURVE_SECRETKEY:
return curveSecretKey;
case ZMQ.ZMQ_CONFLATE:
return conflate;
case ZMQ.ZMQ_GSSAPI_SERVER:
return asServer && mechanism == Mechanisms.GSSAPI;
case ZMQ.ZMQ_GSSAPI_PRINCIPAL:
return gssPrincipal;
case ZMQ.ZMQ_GSSAPI_SERVICE_PRINCIPAL:
return gssServicePrincipal;
case ZMQ.ZMQ_GSSAPI_PLAINTEXT:
return gssPlaintext;
case ZMQ.ZMQ_HANDSHAKE_IVL:
return handshakeIvl;
case ZMQ.ZMQ_HEARTBEAT_IVL:
return heartbeatInterval;
case ZMQ.ZMQ_HEARTBEAT_TIMEOUT:
return heartbeatTimeout;
case ZMQ.ZMQ_HEARTBEAT_TTL:
// Convert the internal deciseconds value to milliseconds
return heartbeatTtl * 100;
case ZMQ.ZMQ_HEARTBEAT_CONTEXT:
return heartbeatContext;
case ZMQ.ZMQ_MSG_ALLOCATOR:
return allocator;
case ZMQ.ZMQ_MSG_ALLOCATION_HEAP_THRESHOLD:
if (allocator instanceof MsgAllocatorThreshold) {
MsgAllocatorThreshold all = (MsgAllocatorThreshold) allocator;
return all.threshold;
}
return -1;
case ZMQ.ZMQ_SELECTOR_PROVIDERCHOOSER:
return selectorChooser;
case ZMQ.ZMQ_AS_TYPE:
return asType;
case ZMQ.ZMQ_SELFADDR_PROPERTY_NAME:
return selfAddressPropertyName;
default:
throw new IllegalArgumentException("option=" + option);
}
}
public static boolean parseBoolean(int option, Object optval)
{
if (optval instanceof Boolean) {
return (Boolean) optval;
}
else if (optval instanceof Integer) {
return (Integer) optval != 0;
}
throw new IllegalArgumentException(optval + " is neither an integer or a boolean for option " + option);
}
public static String parseString(int option, Object optval)
{
if (optval instanceof String) {
return (String) optval;
}
else if (optval instanceof byte[]) {
return new String((byte[]) optval, ZMQ.CHARSET);
}
throw new IllegalArgumentException(optval + " is neither a string or an array of bytes for option " + option);
}
public static byte[] parseBytes(int option, Object optval)
{
if (optval instanceof String) {
return ((String) optval).getBytes(ZMQ.CHARSET);
}
else if (optval instanceof byte[]) {
return (byte[]) optval;
}
throw new IllegalArgumentException(optval + " is neither a string or an array of bytes for option " + option);
}
}
jeromq-0.6.0/src/main/java/zmq/Own.java 0000664 0000000 0000000 00000016652 14557711263 0017710 0 ustar 00root root 0000000 0000000 package zmq;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import zmq.io.IOThread;
import zmq.util.Errno;
// Base class for objects forming a part of ownership hierarchy.
// It handles initialization and destruction of such objects.
public abstract class Own extends ZObject
{
protected final Options options;
// True if termination was already initiated. If so, we can destroy
// the object if there are no more child objects or pending term acks.
private boolean terminating;
// Sequence number of the last command sent to this object.
private final AtomicLong sendSeqnum;
// Sequence number of the last command processed by this object.
private long processedSeqnum;
// Socket owning this object. It's responsible for shutting down
// this object.
private Own owner;
// List of all objects owned by this socket. We are responsible
// for deallocating them before we quit.
//typedef std::set owned_t;
private final Set owned;
// Number of events we have to get before we can destroy the object.
private int termAcks;
public final Errno errno;
// Note that the owner is unspecified in the constructor.
// It'll be supplied later on when the object is plugged in.
// The object is not living within an I/O thread. It has it's own
// thread outside of 0MQ infrastructure.
protected Own(Ctx parent, int tid)
{
super(parent, tid);
terminating = false;
sendSeqnum = new AtomicLong(0);
processedSeqnum = 0;
owner = null;
termAcks = 0;
options = new Options();
errno = options.errno;
owned = new HashSet<>();
}
// The object is living within I/O thread.
protected Own(IOThread ioThread, Options options)
{
super(ioThread);
this.options = options;
terminating = false;
sendSeqnum = new AtomicLong(0);
processedSeqnum = 0;
owner = null;
termAcks = 0;
errno = options.errno;
owned = new HashSet<>();
}
protected abstract void destroy();
// A place to hook in when physical destruction of the object
// is to be delayed.
protected void processDestroy()
{
destroy();
}
private void setOwner(Own owner)
{
assert (this.owner == null) : this.owner;
this.owner = owner;
}
// When another owned object wants to send command to this object
// it calls this function to let it know it should not shut down
// before the command is delivered.
protected void incSeqnum()
{
// This function may be called from a different thread!
sendSeqnum.incrementAndGet();
}
@Override
protected final void processSeqnum()
{
// Catch up with counter of processed commands.
processedSeqnum++;
// We may have caught up and still have pending terms acks.
checkTermAcks();
}
// Launch the supplied object and become its owner.
protected final void launchChild(Own object)
{
// Specify the owner of the object.
object.setOwner(this);
// Plug the object into the I/O thread.
sendPlug(object);
// Take ownership of the object.
sendOwn(this, object);
}
// Terminate owned object
protected final void termChild(Own object)
{
processTermReq(object);
}
@Override
protected final void processTermReq(Own object)
{
// When shutting down we can ignore termination requests from owned
// objects. The termination request was already sent to the object.
if (terminating) {
return;
}
// If I/O object is well and alive let's ask it to terminate.
// If not found, we assume that termination request was already sent to
// the object so we can safely ignore the request.
if (!owned.remove(object)) {
return;
}
registerTermAcks(1);
// Note that this object is the root of the (partial shutdown) thus, its
// value of linger is used, rather than the value stored by the children.
sendTerm(object, options.linger);
}
@Override
protected final void processOwn(Own object)
{
// If the object is already being shut down, new owned objects are
// immediately asked to terminate. Note that linger is set to zero.
if (terminating) {
registerTermAcks(1);
sendTerm(object, 0);
return;
}
// Store the reference to the owned object.
owned.add(object);
}
// Ask owner object to terminate this object. It may take a while
// while actual termination is started. This function should not be
// called more than once.
protected final void terminate()
{
// If termination is already underway, there's no point
// in starting it anew.
if (terminating) {
return;
}
// As for the root of the ownership tree, there's no one to terminate it,
// so it has to terminate itself.
if (owner == null) {
processTerm(options.linger);
return;
}
// If I am an owned object, I'll ask my owner to terminate me.
sendTermReq(owner, this);
}
// Returns true if the object is in process of termination.
protected final boolean isTerminating()
{
return terminating;
}
// Term handler is protected rather than private so that it can
// be intercepted by the derived class. This is useful to add custom
// steps to the beginning of the termination process.
@Override
protected void processTerm(int linger)
{
// Double termination should never happen.
assert (!terminating);
// Send termination request to all owned objects.
for (Own it : owned) {
sendTerm(it, linger);
}
registerTermAcks(owned.size());
owned.clear();
// Start termination process and check whether by chance we cannot
// terminate immediately.
terminating = true;
checkTermAcks();
}
// Use following two functions to wait for arbitrary events before
// terminating. Just add number of events to wait for using
// register_tem_acks functions. When event occurs, call
// remove_term_ack. When number of pending acks reaches zero
// object will be deallocated.
final void registerTermAcks(int count)
{
termAcks += count;
}
final void unregisterTermAck()
{
assert (termAcks > 0);
termAcks--;
// This may be a last ack we are waiting for before termination...
checkTermAcks();
}
@Override
protected final void processTermAck()
{
unregisterTermAck();
}
private void checkTermAcks()
{
if (terminating && processedSeqnum == sendSeqnum.get() && termAcks == 0) {
// Sanity check. There should be no active children at this point.
assert (owned.isEmpty()) : owned;
// The root object has nobody to confirm the termination to.
// Other nodes will confirm the termination to the owner.
if (owner != null) {
sendTermAck(owner);
}
// Deallocate the resources.
processDestroy();
}
}
}
jeromq-0.6.0/src/main/java/zmq/Proxy.java 0000664 0000000 0000000 00000013164 14557711263 0020261 0 ustar 00root root 0000000 0000000 package zmq;
import java.nio.channels.Selector;
import java.util.Arrays;
import zmq.poll.PollItem;
class Proxy
{
public static boolean proxy(SocketBase frontend, SocketBase backend, SocketBase capture)
{
return new Proxy().start(frontend, backend, capture, null);
}
public static boolean proxy(SocketBase frontend, SocketBase backend, SocketBase capture, SocketBase control)
{
return new Proxy().start(frontend, backend, capture, control);
}
public enum State
{
ACTIVE,
PAUSED,
TERMINATED
}
private State state;
private Proxy()
{
state = State.ACTIVE;
}
private boolean start(SocketBase frontend, SocketBase backend, SocketBase capture, SocketBase control)
{
// The algorithm below assumes ratio of requests and replies processed
// under full load to be 1:1.
// TODO: The current implementation drops messages when
// any of the pipes becomes full.
int rc;
int more;
Msg msg;
int count = control == null ? 2 : 3;
PollItem[] items = new PollItem[count];
items[0] = new PollItem(frontend, ZMQ.ZMQ_POLLIN);
items[1] = new PollItem(backend, ZMQ.ZMQ_POLLIN);
if (control != null) {
items[2] = new PollItem(control, ZMQ.ZMQ_POLLIN);
}
PollItem[] itemsout = new PollItem[2];
itemsout[0] = new PollItem(frontend, ZMQ.ZMQ_POLLOUT);
itemsout[1] = new PollItem(backend, ZMQ.ZMQ_POLLOUT);
Selector selector = frontend.getCtx().createSelector();
try {
while (state != State.TERMINATED) {
// Wait while there are either requests or replies to process.
rc = ZMQ.poll(selector, items, -1);
if (rc < 0) {
return false;
}
// Get the pollout separately because when combining this with pollin it makes the CPU
// because pollout shall most of the time return directly.
// POLLOUT is only checked when frontend and backend sockets are not the same.
if (frontend != backend) {
rc = ZMQ.poll(selector, itemsout, 0L);
if (rc < 0) {
return false;
}
}
// Process a control command if any
if (control != null && items[2].isReadable()) {
msg = control.recv(0);
if (msg == null) {
return false;
}
more = control.getSocketOpt(ZMQ.ZMQ_RCVMORE);
if (more < 0) {
return false;
}
// Copy message to capture socket if any
boolean success = capture(capture, msg, more);
if (!success) {
return false;
}
byte[] command = msg.data();
if (Arrays.equals(command, ZMQ.PROXY_PAUSE)) {
state = State.PAUSED;
}
else if (Arrays.equals(command, ZMQ.PROXY_RESUME)) {
state = State.ACTIVE;
}
else if (Arrays.equals(command, ZMQ.PROXY_TERMINATE)) {
state = State.TERMINATED;
}
else {
// This is an API error, we should assert
System.out
.printf("E: invalid command sent to proxy '%s'%n", new String(command, ZMQ.CHARSET));
assert false;
}
}
// Process a request.
if (process(items[0], itemsout[1], frontend, backend)) {
if (!forward(frontend, backend, capture)) {
return false;
}
}
// Process a reply.
if (process(items[1], itemsout[0], frontend, backend)) {
if (!forward(backend, frontend, capture)) {
return false;
}
}
}
}
finally {
frontend.getCtx().closeSelector(selector);
}
return true;
}
private boolean process(PollItem read, PollItem write, SocketBase frontend, SocketBase backend)
{
return state == State.ACTIVE && read.isReadable() && (frontend == backend || write.isWritable());
}
private boolean forward(SocketBase from, SocketBase to, SocketBase capture)
{
int more;
boolean success;
while (true) {
Msg msg = from.recv(0);
if (msg == null) {
return false;
}
more = from.getSocketOpt(ZMQ.ZMQ_RCVMORE);
if (more < 0) {
return false;
}
// Copy message to capture socket if any
success = capture(capture, msg, more);
if (!success) {
return false;
}
success = to.send(msg, more > 0 ? ZMQ.ZMQ_SNDMORE : 0);
if (!success) {
return false;
}
if (more == 0) {
break;
}
}
return true;
}
private boolean capture(SocketBase capture, Msg msg, int more)
{
if (capture != null) {
Msg ctrl = new Msg(msg);
return capture.send(ctrl, more > 0 ? ZMQ.ZMQ_SNDMORE : 0);
}
return true;
}
}
jeromq-0.6.0/src/main/java/zmq/Reaper.java 0000664 0000000 0000000 00000005175 14557711263 0020361 0 ustar 00root root 0000000 0000000 package zmq;
import java.io.Closeable;
import java.io.IOException;
import java.nio.channels.SelectableChannel;
import java.util.concurrent.atomic.AtomicBoolean;
import zmq.poll.IPollEvents;
import zmq.poll.Poller;
final class Reaper extends ZObject implements IPollEvents, Closeable
{
// Reaper thread accesses incoming commands via this mailbox.
private final Mailbox mailbox;
// Handle associated with mailbox' file descriptor.
private final Poller.Handle mailboxHandle;
// I/O multiplexing is performed using a poller object.
private final Poller poller;
// Number of sockets being reaped at the moment.
private int socketsReaping;
// If true, we were already asked to terminate.
private final AtomicBoolean terminating = new AtomicBoolean();
Reaper(Ctx ctx, int tid)
{
super(ctx, tid);
socketsReaping = 0;
String name = "reaper-" + tid;
poller = new Poller(ctx, name);
mailbox = new Mailbox(ctx, name, tid);
SelectableChannel fd = mailbox.getFd();
mailboxHandle = poller.addHandle(fd, this);
poller.setPollIn(mailboxHandle);
}
@Override
public void close() throws IOException
{
poller.destroy();
mailbox.close();
}
Mailbox getMailbox()
{
return mailbox;
}
void start()
{
poller.start();
}
void stop()
{
if (!terminating.get()) {
sendStop();
}
}
@Override
public void inEvent()
{
while (true) {
// Get the next command. If there is none, exit.
Command cmd = mailbox.recv(0);
if (cmd == null) {
break;
}
// Process the command.
cmd.process();
}
}
@Override
protected void processStop()
{
terminating.set(true);
// If there are no sockets being reaped finish immediately.
if (socketsReaping == 0) {
finishTerminating();
}
}
@Override
protected void processReap(SocketBase socket)
{
++socketsReaping;
// Add the socket to the poller.
socket.startReaping(poller);
}
@Override
protected void processReaped()
{
--socketsReaping;
// If reaped was already asked to terminate and there are no more sockets,
// finish immediately.
if (socketsReaping == 0 && terminating.get()) {
finishTerminating();
}
}
private void finishTerminating()
{
sendDone();
poller.removeHandle(mailboxHandle);
poller.stop();
}
}
jeromq-0.6.0/src/main/java/zmq/Signaler.java 0000664 0000000 0000000 00000014235 14557711263 0020704 0 ustar 00root root 0000000 0000000 package zmq;
import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.Pipe;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.concurrent.atomic.AtomicLong;
import zmq.util.Errno;
import zmq.util.Utils;
// This is a cross-platform equivalent to signal_fd. However, as opposed
// to signal_fd there can be at most one signal in the signaler at any
// given moment. Attempt to send a signal before receiving the previous
// one will result in undefined behaviour.
final class Signaler implements Closeable
{
private interface IoOperation
{
O call() throws IOException;
}
// Underlying write & read file descriptor.
private final Pipe.SinkChannel w;
private final Pipe.SourceChannel r;
private final Selector selector;
private final ThreadLocal wdummy = ThreadLocal.withInitial(() -> ByteBuffer.allocate(1));
private final ThreadLocal rdummy = ThreadLocal.withInitial(() -> ByteBuffer.allocate(1));
// Selector.selectNow at every sending message doesn't show enough performance
private final AtomicLong wcursor = new AtomicLong(0);
private long rcursor = 0;
private final Errno errno;
private final int pid;
private final Ctx ctx;
Signaler(Ctx ctx, int pid, Errno errno)
{
this.ctx = ctx;
this.pid = pid;
this.errno = errno;
// Create the socket pair for signaling.
try {
Pipe pipe = Pipe.open();
r = pipe.source();
w = pipe.sink();
// Set both fds to non-blocking mode.
Utils.unblockSocket(w, r);
selector = ctx.createSelector();
r.register(selector, SelectionKey.OP_READ);
}
catch (IOException e) {
throw new ZError.IOException(e);
}
}
private O maksInterrupt(IoOperation operation) throws IOException
{
// This loop try to protect the current thread from external interruption.
// If it happens, it mangles current context internal state.
// So it keep trying until it succeed.
// This must only be called on internal IO (using Pipe)
boolean interrupted = Thread.interrupted();
while (true) {
try {
return operation.call();
}
catch (ClosedByInterruptException e) {
interrupted = true;
}
finally {
if (interrupted) {
Thread.currentThread().interrupt();
}
}
}
}
@Override
public void close() throws IOException
{
IOException exception = null;
IoOperation op1 = () -> {
r.close();
return null;
};
IoOperation op2 = () -> {
w.close();
return null;
};
IoOperation op3 = () -> {
ctx.closeSelector(selector);
return null;
};
for (IoOperation> op : new IoOperation>[] {op1, op2, op3}) {
try {
maksInterrupt(op);
}
catch (IOException e) {
if (exception != null) {
e.addSuppressed(exception);
}
exception = e;
}
}
if (exception != null) {
throw exception;
}
}
SelectableChannel getFd()
{
return r;
}
void send()
{
int nbytes = 0;
while (nbytes == 0) {
try {
wdummy.get().clear();
nbytes = maksInterrupt(() -> w.write(wdummy.get()));
}
catch (IOException e) {
throw new ZError.IOException(e);
}
}
wcursor.incrementAndGet();
}
boolean waitEvent(long timeout)
{
// Transform a interrupt signal in an errno EINTR
if (Thread.interrupted()) {
errno.set(ZError.EINTR);
return false;
}
int rc;
boolean brc = (rcursor < wcursor.get());
if (brc) {
return true;
}
try {
if (timeout == 0) {
// waitEvent(0) is called every read/send of SocketBase
// instant readiness is not strictly required
// On the other hand, we can save lots of system call and increase performance
errno.set(ZError.EAGAIN);
return false;
}
else if (timeout < 0) {
rc = selector.select(0);
}
else {
rc = selector.select(timeout);
}
}
catch (ClosedSelectorException e) {
errno.set(ZError.EINTR);
return false;
}
catch (IOException e) {
errno.set(ZError.exccode(e));
return false;
}
if (Thread.interrupted() || (rc == 0 && timeout <= 0 && ! selector.keys().isEmpty())) {
errno.set(ZError.EINTR);
return false;
}
else if (rc == 0) {
errno.set(ZError.EAGAIN);
return false;
}
selector.selectedKeys().clear();
return true;
}
void recv()
{
int nbytes = 0;
// On windows, there may be a need to try several times until it succeeds
while (nbytes == 0) {
try {
rdummy.get().clear();
nbytes = maksInterrupt(() -> r.read(rdummy.get()));
}
catch (ClosedChannelException e) {
errno.set(ZError.EINTR);
return;
}
catch (IOException e) {
throw new ZError.IOException(e);
}
}
assert (nbytes == 1);
rcursor++;
}
@Override
public String toString()
{
return "Signaler[" + pid + "]";
}
}
jeromq-0.6.0/src/main/java/zmq/SocketBase.java 0000664 0000000 0000000 00000145050 14557711263 0021163 0 ustar 00root root 0000000 0000000 package zmq;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SocketChannel;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import zmq.io.IOThread;
import zmq.io.SessionBase;
import zmq.io.net.Address;
import zmq.io.net.Address.IZAddress;
import zmq.io.net.Listener;
import zmq.io.net.NetProtocol;
import zmq.pipe.Pipe;
import zmq.poll.IPollEvents;
import zmq.poll.Poller;
import zmq.socket.Sockets;
import zmq.util.Blob;
import zmq.util.Clock;
import zmq.util.MultiMap;
public abstract class SocketBase extends Own implements IPollEvents, Pipe.IPipeEvents
{
private static class EndpointPipe
{
private final Own endpoint;
private final Pipe pipe;
public EndpointPipe(Own endpoint, Pipe pipe)
{
super();
this.endpoint = endpoint;
this.pipe = pipe;
}
@Override
public String toString()
{
return "EndpointPipe [endpoint=" + endpoint + ", pipe=" + pipe + "]";
}
}
/**
* The old consumer that forward events through a socket
*/
private static class SocketEventHandler implements ZMQ.EventConsummer
{
private final SocketBase monitorSocket;
public SocketEventHandler(SocketBase monitorSocket)
{
this.monitorSocket = monitorSocket;
}
public void consume(ZMQ.Event ev)
{
ev.write(monitorSocket);
}
@Override
public void close()
{
monitorSocket.close();
}
}
// Map of open endpoints.
private final MultiMap endpoints;
// Map of open inproc endpoints.
private final MultiMap inprocs;
// Used to check whether the object is a socket.
private boolean active;
// If true, associated context was already terminated.
private final AtomicBoolean ctxTerminated;
// If processCommand function was called from InEvent function.
private final ThreadLocal isInEventThreadLocal;
// If true, object should have been already destroyed. However,
// destruction is delayed while we unwind the stack to the point
// where it doesn't intersect the object being destroyed.
private final AtomicBoolean destroyed;
// Socket's mailbox object.
private final IMailbox mailbox;
// the attached pipes.
private final Set pipes;
// Reaper's poller and handle of this socket within it.
private Poller poller;
private Poller.Handle handle;
// Timestamp of when commands were processed the last time.
private long lastTsc;
// Number of messages received since last command processing.
private int ticks;
// True if the last message received had MORE flag set.
private boolean rcvmore;
// File descriptor if applicable
private SocketChannel fileDesc;
// Bitmask of events being monitored
private int monitorEvents;
// Next assigned name on a zmq_connect() call used by ROUTER and STREAM socket types
protected String connectRid;
private final AtomicReference monitor;
// Indicate if the socket is thread safe
private final boolean threadSafe;
// Mutex for synchronize access to the socket in thread safe mode
private final ReentrantLock threadSafeSync;
protected SocketBase(Ctx parent, int tid, int sid)
{
this(parent, tid, sid, false);
}
protected SocketBase(Ctx parent, int tid, int sid, boolean threadSafe)
{
super(parent, tid);
active = true;
ctxTerminated = new AtomicBoolean();
isInEventThreadLocal = new ThreadLocal<>();
destroyed = new AtomicBoolean();
lastTsc = 0;
ticks = 0;
rcvmore = false;
monitorEvents = 0;
monitor = new AtomicReference<>(null);
options.socketId = sid;
options.ipv6 = parent.get(ZMQ.ZMQ_IPV6) != 0;
options.linger = parent.get(ZMQ.ZMQ_BLOCKY) != 0 ? -1 : 0;
endpoints = new MultiMap<>();
inprocs = new MultiMap<>();
pipes = new HashSet<>();
this.threadSafe = threadSafe;
this.threadSafeSync = new ReentrantLock();
mailbox = new Mailbox(parent, "socket-" + sid, tid);
}
// Concrete algorithms for the x- methods are to be defined by
// individual socket types.
protected abstract void xattachPipe(Pipe pipe, boolean subscribe2all, boolean isLocallyInitiated);
protected abstract void xpipeTerminated(Pipe pipe);
/**
* @return false if object is not a socket.
*/
boolean isActive()
{
return active;
}
@Override
protected void destroy()
{
synchronized (monitor) {
try {
mailbox.close();
}
catch (IOException ignore) {
}
stopMonitor();
assert (destroyed.get());
}
}
// Returns the mailbox associated with this socket.
final IMailbox getMailbox()
{
return mailbox;
}
// Interrupt blocking call if the socket is stuck in one.
// This function can be called from a different thread!
final void stop()
{
// Called by ctx when it is terminated (zmq_term).
// 'stop' command is sent from the threads that called zmq_term to
// the thread owning the socket. This way, blocking call in the
// owner thread can be interrupted.
sendStop();
}
// Check whether transport protocol, as specified in connect or
// bind, is available and compatible with the socket type.
private NetProtocol checkProtocol(String protocol)
{
try {
// First check out whether the protcol is something we are aware of.
NetProtocol proto = NetProtocol.getProtocol(protocol);
if (!proto.valid) {
errno.set(ZError.EPROTONOSUPPORT);
return proto;
}
// Check whether socket type and transport protocol match.
// Specifically, multicast protocols can't be combined with
// bi-directional messaging patterns (socket types).
if (!proto.compatible(options.type)) {
errno.set(ZError.ENOCOMPATPROTO);
return null;
}
// Protocol is available.
return proto;
}
catch (IllegalArgumentException e) {
errno.set(ZError.EPROTONOSUPPORT);
return null;
}
}
// Register the pipe with this socket.
private void attachPipe(Pipe pipe, boolean isLocallyInitiated)
{
attachPipe(pipe, false, isLocallyInitiated);
}
private void attachPipe(Pipe pipe, boolean subscribe2all, boolean isLocallyInitiated)
{
assert (pipe != null);
// First, register the pipe so that we can terminate it later on.
pipe.setEventSink(this);
pipes.add(pipe);
// Let the derived socket type know about new pipe.
xattachPipe(pipe, subscribe2all, isLocallyInitiated);
// If the socket is already being closed, ask any new pipes to terminate
// straight away.
if (isTerminating()) {
registerTermAcks(1);
pipe.terminate(false);
}
}
public final boolean setSocketOpt(int option, Object optval)
{
lock();
try {
if (ctxTerminated.get()) {
errno.set(ZError.ETERM);
return false;
}
// First, check whether specific socket type overloads the option.
boolean rc = xsetsockopt(option, optval);
if (rc || errno.get() != ZError.EINVAL) {
return rc;
}
// If the socket type doesn't support the option, pass it to
// the generic option parser.
rc = options.setSocketOpt(option, optval);
if (rc) {
errno.set(0);
}
return rc;
}
finally {
unlock();
}
}
public final int getSocketOpt(int option)
{
lock();
try {
if (ctxTerminated.get()) {
errno.set(ZError.ETERM);
return -1;
}
// fast track to avoid boxing
if (option == ZMQ.ZMQ_RCVMORE) {
return rcvmore ? 1 : 0;
}
if (option == ZMQ.ZMQ_EVENTS) {
boolean rc = processCommands(0, false, null);
if (!rc && (errno.get() == ZError.ETERM || errno.get() == ZError.EINTR)) {
return -1;
}
assert (rc);
int val = 0;
if (hasOut()) {
val |= ZMQ.ZMQ_POLLOUT;
}
if (hasIn()) {
val |= ZMQ.ZMQ_POLLIN;
}
return val;
}
Object val = options.getSocketOpt(option);
if (val instanceof Integer) {
return (Integer) val;
}
if (val instanceof Boolean) {
return (Boolean) val ? 1 : 0;
}
throw new IllegalArgumentException(val + " is neither an integer or a boolean for option " + option);
}
finally {
unlock();
}
}
public final Object getSocketOptx(int option)
{
if (ctxTerminated.get()) {
errno.set(ZError.ETERM);
return null;
}
if (option == ZMQ.ZMQ_RCVMORE) {
return rcvmore ? 1 : 0;
}
if (option == ZMQ.ZMQ_FD) {
if (threadSafe) {
// thread safe socket doesn't provide file descriptor
errno.set(ZError.EINVAL);
return null;
}
return ((Mailbox) mailbox).getFd();
}
if (option == ZMQ.ZMQ_EVENTS) {
boolean rc = processCommands(0, false, null);
if (!rc && (errno.get() == ZError.ETERM || errno.get() == ZError.EINTR)) {
return -1;
}
assert (rc);
int val = 0;
if (hasOut()) {
val |= ZMQ.ZMQ_POLLOUT;
}
if (hasIn()) {
val |= ZMQ.ZMQ_POLLIN;
}
return val;
}
// If the socket type doesn't support the option, pass it to
// the generic option parser.
return options.getSocketOpt(option);
}
public final boolean bind(final String addr)
{
lock();
try {
if (ctxTerminated.get()) {
errno.set(ZError.ETERM);
return false;
}
options.mechanism.check(options);
// Process pending commands, if any.
boolean brc = processCommands(0, false, null);
if (!brc) {
return false;
}
SimpleURI uri = SimpleURI.create(addr);
String address = uri.getAddress();
NetProtocol protocol = checkProtocol(uri.getProtocol());
if (protocol == null) {
return false;
}
switch (protocol) {
case inproc: {
Ctx.Endpoint endpoint = new Ctx.Endpoint(this, options);
boolean rc = registerEndpoint(addr, endpoint);
if (rc) {
connectPending(addr, this);
// Save last endpoint URI
options.lastEndpoint = addr;
}
else {
errno.set(ZError.EADDRINUSE);
}
return rc;
}
case pgm:
// continue
case epgm:
// continue
case norm:
// For convenience's sake, bind can be used interchangeable with
// connect for PGM, EPGM and NORM transports.
return connect(addr);
case tcp:
// continue
case ipc:
// continue
case tipc: {
// Remaining transports require to be run in an I/O thread, so at this
// point we'll choose one.
IOThread ioThread = chooseIoThread(options.affinity);
if (ioThread == null) {
errno.set(ZError.EMTHREAD);
return false;
}
Listener listener = protocol.getListener(ioThread, this, options);
boolean rc = listener.setAddress(address);
if (!rc) {
listener.destroy();
eventBindFailed(address, errno.get());
return false;
}
// Save last endpoint URI
options.lastEndpoint = listener.getAddress();
addEndpoint(options.lastEndpoint, listener, null);
return true;
}
default:
throw new IllegalArgumentException(addr);
}
}
finally {
unlock();
}
}
public final boolean connect(String addr)
{
lock();
try {
return connectInternal(addr);
}
finally {
unlock();
}
}
public final int connectPeer(String addr)
{
lock();
try {
if (options.type != ZMQ.ZMQ_PEER && options.type != ZMQ.ZMQ_RAW) {
errno.set(ZError.ENOTSUP);
return 0;
}
boolean rc = connectInternal(addr);
if (!rc) {
return 0;
}
return options.peerLastRoutingId;
}
finally {
unlock();
}
}
private boolean connectInternal(String addr)
{
if (ctxTerminated.get()) {
errno.set(ZError.ETERM);
return false;
}
options.mechanism.check(options);
// Process pending commands, if any.
boolean brc = processCommands(0, false, null);
if (!brc) {
return false;
}
SimpleURI uri = SimpleURI.create(addr);
String address = uri.getAddress();
NetProtocol protocol = checkProtocol(uri.getProtocol());
if (protocol == null || !protocol.valid) {
return false;
}
if (protocol == NetProtocol.inproc) {
// TODO: inproc connect is specific with respect to creating pipes
// as there's no 'reconnect' functionality implemented. Once that
// is in place we should follow generic pipe creation algorithm.
// Find the peer endpoint.
Ctx.Endpoint peer = findEndpoint(addr);
// The total HWM for an inproc connection should be the sum of
// the binder's HWM and the connector's HWM.
int sndhwm = 0;
if (peer.socket == null) {
sndhwm = options.sendHwm;
}
else if (options.sendHwm != 0 && peer.options.recvHwm != 0) {
sndhwm = options.sendHwm + peer.options.recvHwm;
}
int rcvhwm = 0;
if (peer.socket == null) {
rcvhwm = options.recvHwm;
}
else if (options.recvHwm != 0 && peer.options.sendHwm != 0) {
rcvhwm = options.recvHwm + peer.options.sendHwm;
}
// Create a bi-directional pipe to connect the peers.
ZObject[] parents = {this, peer.socket == null ? this : peer.socket};
boolean conflate = options.conflate && (options.type == ZMQ.ZMQ_DEALER || options.type == ZMQ.ZMQ_PULL
|| options.type == ZMQ.ZMQ_PUSH || options.type == ZMQ.ZMQ_PUB || options.type == ZMQ.ZMQ_SUB);
int[] hwms = {conflate ? -1 : sndhwm, conflate ? -1 : rcvhwm};
boolean[] conflates = {conflate, conflate};
Pipe[] pipes = Pipe.pair(parents, hwms, conflates);
// Attach local end of the pipe to this socket object.
attachPipe(pipes[0], true);
if (peer.socket == null) {
// The peer doesn't exist yet so we don't know whether
// to send the identity message or not. To resolve this,
// we always send our identity and drop it later if
// the peer doesn't expect it.
Msg id = new Msg(options.identitySize);
id.put(options.identity, 0, options.identitySize);
id.setFlags(Msg.IDENTITY);
boolean written = pipes[0].write(id);
assert (written);
pipes[0].flush();
// If set, send the hello msg of the local socket to the peer.
if (options.canSendHelloMsg && options.helloMsg != null) {
written = pipes[0].write(options.helloMsg);
assert (written);
pipes[0].flush();
}
pendConnection(addr, new Ctx.Endpoint(this, options), pipes);
}
else {
// If required, send the identity of the peer to the local socket.
if (peer.options.recvIdentity) {
Msg id = new Msg(options.identitySize);
id.put(options.identity, 0, options.identitySize);
id.setFlags(Msg.IDENTITY);
boolean written = pipes[0].write(id);
assert (written);
pipes[0].flush();
}
// If required, send the identity of the local socket to the peer.
if (options.recvIdentity) {
Msg id = new Msg(peer.options.identitySize);
id.put(peer.options.identity, 0, peer.options.identitySize);
id.setFlags(Msg.IDENTITY);
boolean written = pipes[1].write(id);
assert (written);
pipes[1].flush();
}
// If set, send the hello msg of the local socket to the peer.
if (options.canSendHelloMsg && options.helloMsg != null) {
boolean written = pipes[0].write(options.helloMsg);
assert (written);
pipes[0].flush();
}
// If set, send the hello msg of the peer to the local socket.
if (peer.options.canSendHelloMsg && peer.options.helloMsg != null) {
boolean written = pipes[1].write(peer.options.helloMsg);
assert (written);
pipes[1].flush();
}
if (peer.options.canReceiveDisconnectMsg && peer.options.disconnectMsg != null) {
pipes[0].setDisconnectMsg(peer.options.disconnectMsg);
}
// Attach remote end of the pipe to the peer socket. Note that peer's
// seqnum was incremented in findEndpoint function. We don't need it
// increased here.
sendBind(peer.socket, pipes[1], false);
}
// Save last endpoint URI
options.lastEndpoint = addr;
// remember inproc connections for disconnect
inprocs.insert(addr, pipes[0]);
return true;
}
boolean isSingleConnect = options.type == ZMQ.ZMQ_DEALER || options.type == ZMQ.ZMQ_SUB
|| options.type == ZMQ.ZMQ_REQ;
if (isSingleConnect) {
if (endpoints.hasValues(addr)) {
// There is no valid use for multiple connects for SUB-PUB nor
// DEALER-ROUTER nor REQ-REP. Multiple connects produces
// nonsensical results.
return true;
}
}
// Choose the I/O thread to run the session in.
IOThread ioThread = chooseIoThread(options.affinity);
if (ioThread == null) {
errno.set(ZError.EMTHREAD);
return false;
}
Address paddr = new Address(protocol, address);
// Resolve address (if needed by the protocol)
protocol.resolve(paddr, options.ipv6);
// Create session.
SessionBase session = Sockets.createSession(ioThread, true, this, options, paddr);
assert (session != null);
// PGM does not support subscription forwarding; ask for all data to be
// sent to this pipe. (same for NORM, currently?)
boolean subscribe2all = protocol.subscribe2all;
Pipe newpipe = null;
if (options.immediate || subscribe2all) {
// Create a bi-directional pipe.
ZObject[] parents = {this, session};
boolean conflate = options.conflate && (options.type == ZMQ.ZMQ_DEALER || options.type == ZMQ.ZMQ_PULL
|| options.type == ZMQ.ZMQ_PUSH || options.type == ZMQ.ZMQ_PUB || options.type == ZMQ.ZMQ_SUB);
int[] hwms = {conflate ? -1 : options.sendHwm, conflate ? -1 : options.recvHwm};
boolean[] conflates = {conflate, conflate};
Pipe[] pipes = Pipe.pair(parents, hwms, conflates);
// Attach local end of the pipe to the socket object.
attachPipe(pipes[0], subscribe2all, true);
newpipe = pipes[0];
// Attach remote end of the pipe to the session object later on.
session.attachPipe(pipes[1]);
}
// Save last endpoint URI
options.lastEndpoint = paddr.toString();
addEndpoint(addr, session, newpipe);
return true;
}
public boolean disconnectPeer(int routingId)
{
return xdisconnectPeer(routingId);
}
// Creates new endpoint ID and adds the endpoint to the map.
private void addEndpoint(String addr, Own endpoint, Pipe pipe)
{
// Activate the session. Make it a child of this socket.
launchChild(endpoint);
endpoints.insert(addr, new EndpointPipe(endpoint, pipe));
}
public final boolean termEndpoint(String addr)
{
lock();
try {
// Check whether the library haven't been shut down yet.
if (ctxTerminated.get()) {
errno.set(ZError.ETERM);
return false;
}
// Check whether endpoint address passed to the function is valid.
if (addr == null) {
errno.set(ZError.EINVAL);
return false;
}
// Process pending commands, if any, since there could be pending unprocessed processOwn()'s
// (from launchChild() for example) we're asked to terminate now.
boolean rc = processCommands(0, false, null);
if (!rc) {
return false;
}
SimpleURI uri = SimpleURI.create(addr);
NetProtocol protocol = checkProtocol(uri.getProtocol());
if (protocol == null) {
return false;
}
// Disconnect an inproc socket
if (protocol == NetProtocol.inproc) {
if (unregisterEndpoint(addr, this)) {
return true;
}
Collection olds = inprocs.remove(addr);
if (olds == null || olds.isEmpty()) {
errno.set(ZError.ENOENT);
return false;
}
else {
for (Pipe old : olds) {
old.sendDisconnectMsg();
old.terminate(true);
}
}
return true;
}
String resolvedAddress = addr;
// The resolved last_endpoint is used as a key in the endpoints map.
// The address passed by the user might not match in the TCP case due to
// IPv4-in-IPv6 mapping (EG: tcp://[::ffff:127.0.0.1]:9999), so try to
// resolve before giving up. Given at this stage we don't know whether a
// socket is connected or bound, try with both.
if (protocol == NetProtocol.tcp) {
boolean endpoint = endpoints.hasValues(resolvedAddress);
if (!endpoint) {
// TODO V4 resolve TCP address when unbinding
IZAddress address = protocol.zresolve(uri.getAddress(), options.ipv6);
resolvedAddress = address.address().toString();
endpoint = endpoints.hasValues(resolvedAddress);
if (!endpoint) {
// no luck, try with local resolution
InetSocketAddress socketAddress = address.resolve(uri.getAddress(), options.ipv6, true);
resolvedAddress = socketAddress.toString();
}
}
}
// Find the endpoints range (if any) corresponding to the addr_ string.
Collection eps = endpoints.remove(resolvedAddress);
if (eps == null || eps.isEmpty()) {
errno.set(ZError.ENOENT);
return false;
}
else {
// If we have an associated pipe, terminate it.
for (EndpointPipe ep : eps) {
if (ep.pipe != null) {
ep.pipe.terminate(false);
}
termChild(ep.endpoint);
}
}
return true;
}
finally {
unlock();
}
}
public final boolean send(Msg msg, int flags)
{
return send(msg, flags, null);
}
public final boolean send(Msg msg, int flags, AtomicBoolean canceled)
{
lock();
try {
// Check whether the library haven't been shut down yet.
if (ctxTerminated.get()) {
errno.set(ZError.ETERM);
return false;
}
// Check whether message passed to the function is valid.
if (msg == null || !msg.check()) {
errno.set(ZError.EFAULT);
return false;
}
// Process pending commands, if any.
boolean brc = processCommands(0, true, canceled);
if (!brc) {
return false;
}
// Clear any user-visible flags that are set on the message.
msg.resetFlags(Msg.MORE);
// At this point we impose the flags on the message.
if ((flags & ZMQ.ZMQ_SNDMORE) > 0) {
msg.setFlags(Msg.MORE);
}
msg.resetMetadata();
// Try to send the message.
boolean rc = xsend(msg);
if (rc) {
return true;
}
if (errno.get() != ZError.EAGAIN) {
return false;
}
// In case of non-blocking send we'll simply propagate
// the error - including EAGAIN - up the stack.
if ((flags & ZMQ.ZMQ_DONTWAIT) > 0 || options.sendTimeout == 0) {
return false;
}
// Compute the time when the timeout should occur.
// If the timeout is infinite, don't care.
int timeout = options.sendTimeout;
long end = timeout < 0 ? 0 : (Clock.nowMS() + timeout);
// Oops, we couldn't send the message. Wait for the next
// command, process it and try to send the message again.
// If timeout is reached in the meantime, return EAGAIN.
while (true) {
if (!processCommands(timeout, false, canceled)) {
return false;
}
rc = xsend(msg);
if (rc) {
break;
}
if (errno.get() != ZError.EAGAIN) {
return false;
}
if (timeout > 0) {
timeout = (int) (end - Clock.nowMS());
if (timeout <= 0) {
errno.set(ZError.EAGAIN);
return false;
}
}
}
return true;
}
finally {
unlock();
}
}
public final Msg recv(int flags)
{
return recv(flags, null);
}
public final Msg recv(int flags, AtomicBoolean canceled)
{
lock();
try {
// Check whether the library haven't been shut down yet.
if (ctxTerminated.get()) {
errno.set(ZError.ETERM);
return null;
}
// Check whether message passed to the function is valid.: NOT APPLICABLE
// Once every inbound_poll_rate messages check for signals and process
// incoming commands. This happens only if we are not polling altogether
// because there are messages available all the time. If poll occurs,
// ticks is set to zero and thus we avoid this code.
//
// Note that 'recv' uses different command throttling algorithm (the one
// described above) from the one used by 'send'. This is because counting
// ticks is more efficient than doing RDTSC all the time.
if (++ticks == Config.INBOUND_POLL_RATE.getValue()) {
if (!processCommands(0, false, canceled)) {
return null;
}
ticks = 0;
}
// Get the message.
Msg msg = xrecv();
if (msg == null && errno.get() != ZError.EAGAIN) {
return null;
}
// If we have the message, return immediately.
if (msg != null) {
if (fileDesc != null) {
msg.setFd(fileDesc);
}
extractFlags(msg);
return msg;
}
// If the message cannot be fetched immediately, there are two scenarios.
// For non-blocking recv, commands are processed in case there's an
// activate_reader command already waiting in a command pipe.
// If it's not, return EAGAIN.
if ((flags & ZMQ.ZMQ_DONTWAIT) > 0 || options.recvTimeout == 0) {
if (!processCommands(0, false, canceled)) {
return null;
}
ticks = 0;
msg = xrecv();
if (msg == null) {
return null;
}
extractFlags(msg);
return msg;
}
// Compute the time when the timeout should occur.
// If the timeout is infinite, don't care.
int timeout = options.recvTimeout;
long end = timeout < 0 ? 0 : (Clock.nowMS() + timeout);
// In blocking scenario, commands are processed over and over again until
// we are able to fetch a message.
boolean block = (ticks != 0);
while (true) {
if (!processCommands(block ? timeout : 0, false, canceled)) {
return null;
}
msg = xrecv();
if (msg != null) {
ticks = 0;
break;
}
if (errno.get() != ZError.EAGAIN) {
return null;
}
block = true;
if (timeout > 0) {
timeout = (int) (end - Clock.nowMS());
if (timeout <= 0) {
errno.set(ZError.EAGAIN);
return null;
}
}
}
extractFlags(msg);
return msg;
}
finally {
unlock();
}
}
public final boolean join(String group)
{
lock();
try {
return xjoin(group);
}
finally {
unlock();
}
}
public final boolean leave(String group)
{
lock();
try {
return xleave(group);
}
finally {
unlock();
}
}
public final void cancel(AtomicBoolean canceled)
{
canceled.set(true);
sendCancel();
}
public final int poll(int interest, int timeout, AtomicBoolean canceled)
{
lock();
try {
if (ctxTerminated.get()) {
errno.set(ZError.ETERM);
return -1;
}
int ready = 0;
if (hasOut() && (interest & ZMQ.ZMQ_POLLOUT) > 0) {
ready |= ZMQ.ZMQ_POLLOUT;
}
if (hasIn() && (interest & ZMQ.ZMQ_POLLIN) > 0) {
ready |= ZMQ.ZMQ_POLLIN;
}
if (ready != 0) {
return ready;
}
long end = timeout < 0 ? 0 : (Clock.nowMS() + timeout);
while (true) {
if (!processCommands(timeout, false, canceled)) {
return -1;
}
ready = 0;
if (hasOut() && (interest & ZMQ.ZMQ_POLLOUT) > 0) {
ready |= ZMQ.ZMQ_POLLOUT;
}
if (hasIn() && (interest & ZMQ.ZMQ_POLLIN) > 0) {
ready |= ZMQ.ZMQ_POLLIN;
}
if (ready != 0) {
return ready;
}
if (timeout == 0) {
errno.set(ZError.EAGAIN);
return -1;
}
else if (timeout > 0) {
timeout = (int) (end - Clock.nowMS());
if (timeout <= 0) {
errno.set(ZError.EAGAIN);
return -1;
}
}
}
}
finally {
unlock();
}
}
public final void close()
{
lock();
try {
// Mark the socket as dead
active = false;
// Transfer the ownership of the socket from this application thread
// to the reaper thread which will take care of the rest of shutdown
// process.
sendReap(this);
}
finally {
unlock();
}
}
// These functions are used by the polling mechanism to determine
// which events are to be reported from this socket.
final boolean hasIn()
{
return xhasIn();
}
final boolean hasOut()
{
return xhasOut();
}
// Using this function reaper thread ask the socket to register with
// its poller.
final void startReaping(Poller poller)
{
// Plug the socket to the reaper thread.
this.poller = poller;
SelectableChannel fd;
fd = ((Mailbox) mailbox).getFd();
handle = this.poller.addHandle(fd, this);
this.poller.setPollIn(handle);
// Initialize the termination and check whether it can be deallocated
// immediately.
ctxTerminated.set(true);
terminate();
checkDestroy();
}
private boolean isInEvent()
{
Boolean bRes = isInEventThreadLocal.get();
return null != bRes && bRes;
}
// Processes commands sent to this socket (if any). If timeout is -1,
// returns only after at least one command was processed.
// If throttle argument is true, commands are processed at most once
// in a predefined time period.
private boolean processCommands(int timeout, boolean throttle, AtomicBoolean canceled)
{
Command cmd;
if (timeout != 0) {
// If we are asked to wait, simply ask mailbox to wait.
cmd = mailbox.recv(timeout);
}
else {
// If we are asked not to wait, check whether we haven't processed
// commands recently, so that we can throttle the new commands.
// Get the CPU's tick counter. If 0, the counter is not available.
long tsc = 0; // Clock.rdtsc();
// Optimized version of command processing - it doesn't have to check
// for incoming commands each time. It does so only if certain time
// elapsed since last command processing. Command delay varies
// depending on CPU speed: It's ~1ms on 3GHz CPU, ~2ms on 1.5GHz CPU
// etc. The optimization makes sense only on platforms where getting
// a timestamp is a very cheap operation (tens of nanoseconds).
if (tsc != 0 && throttle) {
// Check whether TSC haven't jumped backwards (in case of migration
// between CPU cores) and whether certain time have elapsed since
// last command processing. If it didn't do nothing.
if (tsc >= lastTsc && tsc - lastTsc <= Config.MAX_COMMAND_DELAY.getValue()) {
return true;
}
lastTsc = tsc;
}
// Check whether there are any commands pending for this thread.
cmd = mailbox.recv(0);
}
// Process all the commands available at the moment.
while (cmd != null) {
cmd.process();
cmd = mailbox.recv(0);
}
if (!isInEvent() && destroyed.get()) {
sendReapAck();
}
if (errno.get() == ZError.EINTR) {
return false;
}
if (canceled != null && canceled.get()) {
errno.set(ZError.ECANCELED);
return false;
}
assert (errno.get() == ZError.EAGAIN) : errno;
if (ctxTerminated.get()) {
errno.set(ZError.ETERM); // Do not raise exception at the blocked operation
return false;
}
return true;
}
@Override
protected final void processStop()
{
// Here, someone have called zmq_term while the socket was still alive.
// We'll remember the fact so that any blocking call is interrupted and any
// further attempt to use the socket will return ETERM. The user is still
// responsible for calling zmq_close on the socket though!
synchronized (monitor) {
stopMonitor();
ctxTerminated.set(true);
}
}
@Override
protected final void processBind(Pipe pipe)
{
attachPipe(pipe, false);
}
@Override
protected final void processTerm(int linger)
{
// Unregister all inproc endpoints associated with this socket.
// Doing this we make sure that no new pipes from other sockets (inproc)
// will be initiated.
unregisterEndpoints(this);
// Ask all attached pipes to terminate.
for (Pipe pipe : pipes) {
pipe.sendDisconnectMsg();
pipe.terminate(false);
}
registerTermAcks(pipes.size());
// Continue the termination process immediately.
super.processTerm(linger);
}
// Delay actual destruction of the socket.
@Override
protected final void processDestroy()
{
destroyed.set(true);
}
// The default implementation assumes there are no specific socket
// options for the particular socket type. If not so, overload this
// method.
protected boolean xsetsockopt(int option, Object optval)
{
errno.set(ZError.EINVAL);
return false;
}
protected boolean xhasOut()
{
return false;
}
protected boolean xsend(Msg msg)
{
throw new UnsupportedOperationException("Must Override");
}
protected boolean xhasIn()
{
return false;
}
protected Msg xrecv()
{
throw new UnsupportedOperationException("Must Override");
}
protected Blob getCredential()
{
throw new UnsupportedOperationException("Must Override");
}
protected void xreadActivated(Pipe pipe)
{
throw new UnsupportedOperationException("Must Override");
}
protected void xwriteActivated(Pipe pipe)
{
throw new UnsupportedOperationException("Must Override");
}
protected void xhiccuped(Pipe pipe)
{
throw new UnsupportedOperationException("Must override");
}
protected boolean xjoin(String group)
{
throw new UnsupportedOperationException("Must override");
}
protected boolean xleave(String group)
{
throw new UnsupportedOperationException("Must override");
}
protected boolean xdisconnectPeer(int routingId)
{
throw new UnsupportedOperationException("Must override");
}
private void enterInEvent()
{
isInEventThreadLocal.set(true);
}
private void leaveInEvent()
{
isInEventThreadLocal.remove();
}
@Override
public final void inEvent()
{
// This function is invoked only once the socket is running in the context
// of the reaper thread. Process any commands from other threads/sockets
// that may be available at the moment. Ultimately, the socket will
// be destroyed.
lock();
try {
enterInEvent();
processCommands(0, false, null);
}
finally {
leaveInEvent();
unlock();
}
checkDestroy();
}
// To be called after processing commands or invoking any command
// handlers explicitly. If required, it will deallocate the socket.
private void checkDestroy()
{
// If the object was already marked as destroyed, finish the deallocation.
if (destroyed.get()) {
// Remove the socket from the reaper's poller.
poller.removeHandle(handle);
// Remove the socket from the context.
destroySocket(this);
// Notify the reaper about the fact.
sendReaped();
// Deallocate.
super.processDestroy();
}
}
@Override
public final void readActivated(Pipe pipe)
{
xreadActivated(pipe);
}
@Override
public final void writeActivated(Pipe pipe)
{
xwriteActivated(pipe);
}
@Override
public final void hiccuped(Pipe pipe)
{
if (!options.immediate) {
pipe.terminate(false);
}
else {
// Notify derived sockets of the hiccup
xhiccuped(pipe);
}
}
@Override
public final void pipeTerminated(Pipe pipe)
{
// Notify the specific socket type about the pipe termination.
xpipeTerminated(pipe);
// Remove pipe from inproc pipes
inprocs.remove(pipe);
// Remove the pipe from the list of attached pipes and confirm its
// termination if we are already shutting down.
pipes.remove(pipe);
if (isTerminating()) {
unregisterTermAck();
}
}
// Moves the flags from the message to local variables,
// to be later retrieved by getSocketOpt.
private void extractFlags(Msg msg)
{
// Test whether IDENTITY flag is valid for this socket type.
assert !msg.isIdentity() || (options.recvIdentity);
// Remove MORE flag.
rcvmore = msg.hasMore();
}
/**
* Register the address for a monitor. It must be a inproc PAIR.
* @param addr or null for unregister.
* @param events an event mask to monitor.
* @return true if creation succeeded.
* @throws IllegalStateException if a previous monitor was already
* registered and consumer is not null.
*/
public final boolean monitor(final String addr, int events)
{
synchronized (monitor) {
// To be tested before trying anything
if (ctxTerminated.get()) {
errno.set(ZError.ETERM);
return false;
}
// Support unregistering monitoring endpoints as well
if (addr == null) {
stopMonitor();
return true;
}
SimpleURI uri = SimpleURI.create(addr);
NetProtocol protocol = checkProtocol(uri.getProtocol());
if (protocol == null) {
return false;
}
// Event notification only supported over inproc://
if (protocol != NetProtocol.inproc) {
errno.set(ZError.EPROTONOSUPPORT);
return false;
}
SocketBase monitorSocket = getCtx().createSocket(ZMQ.ZMQ_PAIR);
if (monitorSocket == null) {
return false;
}
// Never block context termination on pending event messages
monitorSocket.setSocketOpt(ZMQ.ZMQ_LINGER, 0);
boolean rc = monitorSocket.bind(addr);
if (rc) {
return setEventHook(new SocketEventHandler(monitorSocket), events);
}
else {
return false;
}
}
}
/**
* Register a custom event consumer.
* @param consumer or null for unregister.
* @param events an event mask to monitor.
* @return true if creation succeeded.
* @throws IllegalStateException if a previous monitor was already
* registered and consumer is not null.
*/
public final boolean setEventHook(ZMQ.EventConsummer consumer, int events)
{
synchronized (monitor) {
if (ctxTerminated.get()) {
errno.set(ZError.ETERM);
return false;
}
// Support unregistering monitoring endpoints as well
if (consumer == null) {
stopMonitor();
}
else {
if (monitor.get() != null) {
throw new IllegalStateException("Monitor registered twice");
}
monitor.set(consumer);
// Register events to monitor
monitorEvents = events;
}
return true;
}
}
public final void eventHandshaken(String addr, int zmtpVersion)
{
event(addr, zmtpVersion, ZMQ.ZMQ_EVENT_HANDSHAKE_PROTOCOL);
}
public final void eventConnected(String addr, SelectableChannel ch)
{
event(addr, ch, ZMQ.ZMQ_EVENT_CONNECTED);
}
public final void eventConnectDelayed(String addr, int errno)
{
event(addr, errno, ZMQ.ZMQ_EVENT_CONNECT_DELAYED);
}
public final void eventConnectRetried(String addr, int interval)
{
event(addr, interval, ZMQ.ZMQ_EVENT_CONNECT_RETRIED);
}
public final void eventListening(String addr, SelectableChannel ch)
{
event(addr, ch, ZMQ.ZMQ_EVENT_LISTENING);
}
public final void eventBindFailed(String addr, int errno)
{
event(addr, errno, ZMQ.ZMQ_EVENT_BIND_FAILED);
}
public final void eventAccepted(String addr, SelectableChannel ch)
{
event(addr, ch, ZMQ.ZMQ_EVENT_ACCEPTED);
}
public final void eventAcceptFailed(String addr, int errno)
{
event(addr, errno, ZMQ.ZMQ_EVENT_ACCEPT_FAILED);
}
public final void eventClosed(String addr, SelectableChannel ch)
{
event(addr, ch, ZMQ.ZMQ_EVENT_CLOSED);
}
public final void eventCloseFailed(String addr, int errno)
{
event(addr, errno, ZMQ.ZMQ_EVENT_CLOSE_FAILED);
}
public final void eventDisconnected(String addr, SelectableChannel ch)
{
event(addr, ch, ZMQ.ZMQ_EVENT_DISCONNECTED);
}
public final void eventHandshakeFailedNoDetail(String addr, int errno)
{
event(addr, errno, ZMQ.ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL);
}
public final void eventHandshakeFailedProtocol(String addr, int errno)
{
event(addr, errno, ZMQ.ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL);
}
public final void eventHandshakeFailedAuth(String addr, int errno)
{
event(addr, errno, ZMQ.ZMQ_EVENT_HANDSHAKE_FAILED_AUTH);
}
public final void eventHandshakeSucceeded(String addr, int errno)
{
event(addr, errno, ZMQ.ZMQ_EVENT_HANDSHAKE_SUCCEEDED);
}
private void event(String addr, Object arg, int event)
{
synchronized (monitor) {
if ((monitorEvents & event) == 0 || monitor.get() == null) {
return;
}
monitorEvent(new ZMQ.Event(event, addr, arg));
}
}
// Send a monitor event
protected final void monitorEvent(ZMQ.Event event)
{
if (monitor.get() == null) {
return;
}
monitor.get().consume(event);
}
private void stopMonitor()
{
// this is a private method which is only called from
// contexts where the mutex has been locked before
if (monitor.get() != null) {
if ((monitorEvents & ZMQ.ZMQ_EVENT_MONITOR_STOPPED) != 0) {
monitorEvent(new ZMQ.Event(ZMQ.ZMQ_EVENT_MONITOR_STOPPED, "", 0));
}
monitor.get().close();
monitor.set(null);
monitorEvents = 0;
}
}
@Override
public String toString()
{
return getClass().getSimpleName() + "[" + options.socketId + "]";
}
public final SelectableChannel getFD()
{
if (threadSafe) {
errno.set(ZError.EINVAL);
return null;
}
return ((Mailbox) mailbox).getFd();
}
public String typeString()
{
return Sockets.name(options.type);
}
public final int errno()
{
return errno.get();
}
private void lock()
{
if (threadSafe) {
threadSafeSync.lock();
}
}
private void unlock()
{
if (threadSafe) {
threadSafeSync.unlock();
}
}
private static class SimpleURI
{
private final String protocol;
private final String address;
private SimpleURI(String protocol, String address)
{
this.protocol = protocol;
this.address = address;
}
public static SimpleURI create(String value)
{
int pos = value.indexOf("://");
if (pos < 0) {
throw new IllegalArgumentException("Invalid URI: " + value);
}
String protocol = value.substring(0, pos);
String address = value.substring(pos + 3);
if (protocol.isEmpty() || address.isEmpty()) {
throw new IllegalArgumentException("Invalid URI: " + value);
}
return new SimpleURI(protocol, address);
}
public String getProtocol()
{
return protocol;
}
public String getAddress()
{
return address;
}
}
}
jeromq-0.6.0/src/main/java/zmq/Utils.java 0000664 0000000 0000000 00000002533 14557711263 0020236 0 ustar 00root root 0000000 0000000 package zmq;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SocketChannel;
import zmq.io.net.Address;
import zmq.io.net.tcp.TcpUtils;
@Deprecated
public class Utils
{
private Utils()
{
}
public static int randomInt()
{
return zmq.util.Utils.randomInt();
}
public static byte[] randomBytes(int length)
{
return zmq.util.Utils.randomBytes(length);
}
public static int findOpenPort() throws IOException
{
return zmq.util.Utils.findOpenPort();
}
public static void unblockSocket(SelectableChannel... channels) throws IOException
{
TcpUtils.unblockSocket(channels);
}
public static T[] realloc(Class klass, T[] src, int size, boolean ended)
{
return zmq.util.Utils.realloc(klass, src, size, ended);
}
public static byte[] bytes(ByteBuffer buf)
{
return zmq.util.Utils.bytes(buf);
}
public static byte[] realloc(byte[] src, int size)
{
return zmq.util.Utils.realloc(src, size);
}
public static boolean delete(File path)
{
return zmq.util.Utils.delete(path);
}
public static Address getPeerIpAddress(SocketChannel fd)
{
return zmq.util.Utils.getPeerIpAddress(fd);
}
}
jeromq-0.6.0/src/main/java/zmq/ZError.java 0000664 0000000 0000000 00000007244 14557711263 0020365 0 ustar 00root root 0000000 0000000 package zmq;
import java.net.SocketException;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.ClosedChannelException;
import org.zeromq.ZMQ.Error;
import org.zeromq.UncheckedZMQException;
public class ZError
{
private ZError()
{
}
public static class CtxTerminatedException extends UncheckedZMQException
{
private static final long serialVersionUID = -4404921838608052956L;
public CtxTerminatedException()
{
super();
}
}
public static class InstantiationException extends UncheckedZMQException
{
private static final long serialVersionUID = -4404921838608052955L;
public InstantiationException(Throwable cause)
{
super(cause);
}
public InstantiationException(String message, Throwable cause)
{
super(message, cause);
}
public InstantiationException(String message)
{
super(message);
}
}
public static class IOException extends UncheckedZMQException
{
private static final long serialVersionUID = 9202470691157986262L;
public IOException(java.io.IOException e)
{
super(e);
}
}
public static final int ENOENT = 2;
public static final int EINTR = 4;
public static final int EACCESS = 13;
public static final int EFAULT = 14;
public static final int EINVAL = 22;
public static final int EAGAIN = 35;
public static final int EINPROGRESS = 36;
public static final int EPROTONOSUPPORT = 43;
public static final int ENOTSUP = 45;
public static final int EADDRINUSE = 48;
public static final int EADDRNOTAVAIL = 49;
public static final int ENETDOWN = 50;
public static final int ENOBUFS = 55;
public static final int EISCONN = 56;
public static final int ENOTCONN = 57;
public static final int ECONNREFUSED = 61;
public static final int EHOSTUNREACH = 65;
public static final int ECANCELED = 125;
private static final int ZMQ_HAUSNUMERO = 156384712;
public static final int ENOTSOCK = ZMQ_HAUSNUMERO + 5;
public static final int EMSGSIZE = ZMQ_HAUSNUMERO + 10;
public static final int EAFNOSUPPORT = ZMQ_HAUSNUMERO + 11;
public static final int ENETUNREACH = ZMQ_HAUSNUMERO + 12;
public static final int ECONNABORTED = ZMQ_HAUSNUMERO + 13;
public static final int ECONNRESET = ZMQ_HAUSNUMERO + 14;
public static final int ETIMEDOUT = ZMQ_HAUSNUMERO + 16;
public static final int ENETRESET = ZMQ_HAUSNUMERO + 18;
public static final int EFSM = ZMQ_HAUSNUMERO + 51;
public static final int ENOCOMPATPROTO = ZMQ_HAUSNUMERO + 52;
public static final int ETERM = ZMQ_HAUSNUMERO + 53;
public static final int EMTHREAD = ZMQ_HAUSNUMERO + 54;
public static final int EIOEXC = ZMQ_HAUSNUMERO + 105;
public static final int ESOCKET = ZMQ_HAUSNUMERO + 106;
public static final int EMFILE = ZMQ_HAUSNUMERO + 107;
public static final int EPROTO = ZMQ_HAUSNUMERO + 108;
public static int exccode(java.io.IOException e)
{
if (e instanceof SocketException) {
return ESOCKET;
}
else if (e instanceof ClosedByInterruptException) {
return EINTR;
}
else if (e instanceof ClosedChannelException) {
return ENOTCONN;
}
else {
return EIOEXC;
}
}
public static String toString(int code)
{
return Error.findByCode(code).getMessage();
}
}
jeromq-0.6.0/src/main/java/zmq/ZMQ.java 0000664 0000000 0000000 00000116606 14557711263 0017614 0 ustar 00root root 0000000 0000000 package zmq;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
import zmq.io.Metadata;
import zmq.io.mechanism.Mechanisms;
import zmq.io.net.SelectorProviderChooser;
import zmq.msg.MsgAllocator;
import zmq.msg.MsgAllocatorThreshold;
import zmq.poll.PollItem;
import zmq.util.Clock;
import zmq.util.Utils;
public class ZMQ
{
/******************************************************************************/
/* 0MQ versioning support. */
/******************************************************************************/
/* Version macros for compile-time API version detection */
public static final int ZMQ_VERSION_MAJOR = 4;
public static final int ZMQ_VERSION_MINOR = 1;
public static final int ZMQ_VERSION_PATCH = 7;
/* Context options */
public static final int ZMQ_IO_THREADS = 1;
public static final int ZMQ_MAX_SOCKETS = 2;
/* Default for new contexts */
public static final int ZMQ_IO_THREADS_DFLT = 1;
public static final int ZMQ_MAX_SOCKETS_DFLT = 1024;
/******************************************************************************/
/* 0MQ socket definition. */
/******************************************************************************/
/* Socket types. */
public static final int ZMQ_PAIR = 0;
public static final int ZMQ_PUB = 1;
public static final int ZMQ_SUB = 2;
public static final int ZMQ_REQ = 3;
public static final int ZMQ_REP = 4;
public static final int ZMQ_DEALER = 5;
public static final int ZMQ_ROUTER = 6;
public static final int ZMQ_PULL = 7;
public static final int ZMQ_PUSH = 8;
public static final int ZMQ_XPUB = 9;
public static final int ZMQ_XSUB = 10;
public static final int ZMQ_STREAM = 11;
public static final int ZMQ_SERVER = 12;
public static final int ZMQ_CLIENT = 13;
public static final int ZMQ_RADIO = 14;
public static final int ZMQ_DISH = 15;
public static final int ZMQ_CHANNEL = 16;
public static final int ZMQ_PEER = 17;
public static final int ZMQ_RAW = 18;
public static final int ZMQ_SCATTER = 19;
public static final int ZMQ_GATHER = 20;
/* Deprecated aliases */
@Deprecated
public static final int ZMQ_XREQ = ZMQ_DEALER;
@Deprecated
public static final int ZMQ_XREP = ZMQ_ROUTER;
private static final int ZMQ_CUSTOM_OPTION = 1000;
/* Socket options. */
public static final int ZMQ_AFFINITY = 4;
public static final int ZMQ_IDENTITY = 5;
public static final int ZMQ_SUBSCRIBE = 6;
public static final int ZMQ_UNSUBSCRIBE = 7;
public static final int ZMQ_RATE = 8;
public static final int ZMQ_RECOVERY_IVL = 9;
public static final int ZMQ_SNDBUF = 11;
public static final int ZMQ_RCVBUF = 12;
public static final int ZMQ_RCVMORE = 13;
public static final int ZMQ_FD = 14;
public static final int ZMQ_EVENTS = 15;
public static final int ZMQ_TYPE = 16;
public static final int ZMQ_LINGER = 17;
public static final int ZMQ_RECONNECT_IVL = 18;
public static final int ZMQ_BACKLOG = 19;
public static final int ZMQ_RECONNECT_IVL_MAX = 21;
public static final int ZMQ_MAXMSGSIZE = 22;
public static final int ZMQ_SNDHWM = 23;
public static final int ZMQ_RCVHWM = 24;
public static final int ZMQ_MULTICAST_HOPS = 25;
public static final int ZMQ_RCVTIMEO = 27;
public static final int ZMQ_SNDTIMEO = 28;
public static final int ZMQ_LAST_ENDPOINT = 32;
public static final int ZMQ_ROUTER_MANDATORY = 33;
public static final int ZMQ_TCP_KEEPALIVE = 34;
public static final int ZMQ_TCP_KEEPALIVE_CNT = 35;
public static final int ZMQ_TCP_KEEPALIVE_IDLE = 36;
public static final int ZMQ_TCP_KEEPALIVE_INTVL = 37;
public static final int ZMQ_IMMEDIATE = 39 + ZMQ_CUSTOM_OPTION; // for compatibility with ZMQ_DELAY_ATTACH_ON_CONNECT
public static final int ZMQ_XPUB_VERBOSE = 40;
public static final int ZMQ_ROUTER_RAW = 41;
public static final int ZMQ_IPV6 = 42;
public static final int ZMQ_MECHANISM = 43;
public static final int ZMQ_PLAIN_SERVER = 44;
public static final int ZMQ_PLAIN_USERNAME = 45;
public static final int ZMQ_PLAIN_PASSWORD = 46;
public static final int ZMQ_CURVE_SERVER = 47;
public static final int ZMQ_CURVE_PUBLICKEY = 48;
public static final int ZMQ_CURVE_SECRETKEY = 49;
public static final int ZMQ_CURVE_SERVERKEY = 50;
public static final int ZMQ_PROBE_ROUTER = 51;
public static final int ZMQ_REQ_CORRELATE = 52;
public static final int ZMQ_REQ_RELAXED = 53;
public static final int ZMQ_CONFLATE = 54;
public static final int ZMQ_ZAP_DOMAIN = 55;
// TODO: more constants
public static final int ZMQ_ROUTER_HANDOVER = 56;
public static final int ZMQ_TOS = 57;
public static final int ZMQ_CONNECT_RID = 61;
public static final int ZMQ_GSSAPI_SERVER = 62;
public static final int ZMQ_GSSAPI_PRINCIPAL = 63;
public static final int ZMQ_GSSAPI_SERVICE_PRINCIPAL = 64;
public static final int ZMQ_GSSAPI_PLAINTEXT = 65;
public static final int ZMQ_HANDSHAKE_IVL = 66;
public static final int ZMQ_SOCKS_PROXY = 67;
public static final int ZMQ_XPUB_NODROP = 69;
public static final int ZMQ_BLOCKY = 70;
public static final int ZMQ_XPUB_MANUAL = 71;
public static final int ZMQ_HEARTBEAT_IVL = 75;
public static final int ZMQ_HEARTBEAT_TTL = 76;
public static final int ZMQ_HEARTBEAT_TIMEOUT = 77;
public static final int ZMQ_XPUB_VERBOSER = 78;
@Deprecated
public static final int ZMQ_XPUB_VERBOSE_UNSUBSCRIBE = 78;
public static final int ZMQ_HELLO_MSG = 79;
public static final int ZMQ_AS_TYPE = 80;
public static final int ZMQ_DISCONNECT_MSG = 81;
public static final int ZMQ_HICCUP_MSG = 82;
public static final int ZMQ_SELFADDR_PROPERTY_NAME = 83;
/* Custom options */
@Deprecated
public static final int ZMQ_ENCODER = ZMQ_CUSTOM_OPTION + 1;
@Deprecated
public static final int ZMQ_DECODER = ZMQ_CUSTOM_OPTION + 2;
public static final int ZMQ_MSG_ALLOCATOR = ZMQ_CUSTOM_OPTION + 3;
public static final int ZMQ_MSG_ALLOCATION_HEAP_THRESHOLD = ZMQ_CUSTOM_OPTION + 4;
public static final int ZMQ_HEARTBEAT_CONTEXT = ZMQ_CUSTOM_OPTION + 5;
public static final int ZMQ_SELECTOR_PROVIDERCHOOSER = ZMQ_CUSTOM_OPTION + 6;
/* Message options */
public static final int ZMQ_MORE = 1;
/* Send/recv options. */
public static final int ZMQ_DONTWAIT = 1;
public static final int ZMQ_SNDMORE = 2;
/* Deprecated aliases */
@Deprecated
public static final int ZMQ_TCP_ACCEPT_FILTER = 38;
@Deprecated
public static final int ZMQ_IPV4ONLY = 31;
@Deprecated
public static final int ZMQ_DELAY_ATTACH_ON_CONNECT = 39;
@Deprecated
public static final int ZMQ_NOBLOCK = ZMQ_DONTWAIT;
@Deprecated
public static final int ZMQ_FAIL_UNROUTABLE = ZMQ_ROUTER_MANDATORY;
@Deprecated
public static final int ZMQ_ROUTER_BEHAVIOR = ZMQ_ROUTER_MANDATORY;
/******************************************************************************/
/* 0MQ socket events and monitoring */
/******************************************************************************/
/* Socket transport events (tcp and ipc only) */
public static final int ZMQ_EVENT_CONNECTED = 1;
public static final int ZMQ_EVENT_CONNECT_DELAYED = 1 << 1;
public static final int ZMQ_EVENT_CONNECT_RETRIED = 1 << 2;
public static final int ZMQ_EVENT_LISTENING = 1 << 3;
public static final int ZMQ_EVENT_BIND_FAILED = 1 << 4;
public static final int ZMQ_EVENT_ACCEPTED = 1 << 5;
public static final int ZMQ_EVENT_ACCEPT_FAILED = 1 << 6;
public static final int ZMQ_EVENT_CLOSED = 1 << 7;
public static final int ZMQ_EVENT_CLOSE_FAILED = 1 << 8;
public static final int ZMQ_EVENT_DISCONNECTED = 1 << 9;
public static final int ZMQ_EVENT_MONITOR_STOPPED = 1 << 10;
public static final int ZMQ_EVENT_HANDSHAKE_PROTOCOL = 1 << 15;
public static final int ZMQ_EVENT_ALL = 0xffff;
/* Unspecified system errors during handshake. Event value is an errno. */
public static final int ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL = 1 << 11;
/* Handshake complete successfully with successful authentication (if *
* enabled). Event value is unused. */
public static final int ZMQ_EVENT_HANDSHAKE_SUCCEEDED = 1 << 12;
/* Protocol errors between ZMTP peers or between server and ZAP handler. *
* Event value is one of ZMQ_PROTOCOL_ERROR_* */
public static final int ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL = 1 << 13;
/* Failed authentication requests. Event value is the numeric ZAP status *
* code, i.e. 300, 400 or 500. */
public static final int ZMQ_EVENT_HANDSHAKE_FAILED_AUTH = 1 << 14;
public static final int ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED = 0x10000000;
public static final int ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND = 0x10000001;
public static final int ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_SEQUENCE = 0x10000002;
public static final int ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE = 0x10000003;
public static final int ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_UNSPECIFIED = 0x10000011;
public static final int ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE = 0x10000012;
public static final int ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO = 0x10000013;
public static final int ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE = 0x10000014;
public static final int ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR = 0x10000015;
public static final int ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY = 0x10000016;
public static final int ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_WELCOME = 0x10000017;
public static final int ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_METADATA = 0x10000018;
// the following two may be due to erroneous configuration of a peer
public static final int ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC = 0x11000001;
public static final int ZMQ_PROTOCOL_ERROR_ZMTP_MECHANISM_MISMATCH = 0x11000002;
public static final int ZMQ_PROTOCOL_ERROR_ZAP_UNSPECIFIED = 0x20000000;
public static final int ZMQ_PROTOCOL_ERROR_ZAP_MALFORMED_REPLY = 0x20000001;
public static final int ZMQ_PROTOCOL_ERROR_ZAP_BAD_REQUEST_ID = 0x20000002;
public static final int ZMQ_PROTOCOL_ERROR_ZAP_BAD_VERSION = 0x20000003;
public static final int ZMQ_PROTOCOL_ERROR_ZAP_INVALID_STATUS_CODE = 0x20000004;
public static final int ZMQ_PROTOCOL_ERROR_ZAP_INVALID_METADATA = 0x20000005;
public static final int ZMQ_PROTOCOL_ERROR_WS_UNSPECIFIED = 0x30000000;
public static final int ZMQ_POLLIN = 1;
public static final int ZMQ_POLLOUT = 2;
public static final int ZMQ_POLLERR = 4;
@Deprecated
public static final int ZMQ_STREAMER = 1;
@Deprecated
public static final int ZMQ_FORWARDER = 2;
@Deprecated
public static final int ZMQ_QUEUE = 3;
public static final byte[] MESSAGE_SEPARATOR = new byte[0];
public static final byte[] SUBSCRIPTION_ALL = new byte[0];
// Android compatibility: not using StandardCharsets (API 19+)
public static final Charset CHARSET = StandardCharsets.UTF_8;
public static final byte[] PROXY_PAUSE = "PAUSE".getBytes(ZMQ.CHARSET);
public static final byte[] PROXY_RESUME = "RESUME".getBytes(ZMQ.CHARSET);
public static final byte[] PROXY_TERMINATE = "TERMINATE".getBytes(ZMQ.CHARSET);
// Default values for options
/**
* Default value for {@link ZMQ#ZMQ_AFFINITY}
*/
public static final int DEFAULT_AFFINITY = 0;
/**
* Default value for {@link ZMQ#ZMQ_SERVER}
*/
public static final boolean DEFAULT_AS_SERVER = false;
/**
* Default value for {@link ZMQ#ZMQ_AS_TYPE}
*/
public static final int DEFAULT_AS_TYPE = -1;
/**
* Default value for {@link ZMQ#ZMQ_BACKLOG}
*/
public static final int DEFAULT_BACKLOG = 100;
/**
* Default value for {@link ZMQ#ZMQ_CONFLATE}
*/
public static final boolean DEFAULT_CONFLATE = false;
/**
* Default value for {@link ZMQ#ZMQ_DISCONNECT_MSG}
*/
public static final Msg DEFAULT_DISCONNECT_MSG = null;
/**
* Default value for {@link ZMQ#ZMQ_GSSAPI_PLAINTEXT}
*/
public static final boolean DEFAULT_GSS_PLAINTEXT = false;
/**
* Default value for {@link ZMQ#ZMQ_HANDSHAKE_IVL}
*/
public static final int DEFAULT_HANDSHAKE_IVL = 30000;
/**
* Default value for {@link ZMQ#ZMQ_HEARTBEAT_CONTEXT}
*/
public static final byte[] DEFAULT_HEARTBEAT_CONTEXT = new byte[0];
/**
* Default value for {@link ZMQ#ZMQ_HEARTBEAT_IVL}
*/
public static final int DEFAULT_HEARTBEAT_INTERVAL = 0;
/**
* Default value for {@link ZMQ#ZMQ_HEARTBEAT_TIMEOUT}
*/
public static final int DEFAULT_HEARTBEAT_TIMEOUT = -1;
/**
* Default value for {@link ZMQ#ZMQ_HEARTBEAT_TTL}
*/
public static final int DEFAULT_HEARTBEAT_TTL = 0;
/**
* Default value for {@link ZMQ#ZMQ_HELLO_MSG}
*/
public static final Msg DEFAULT_HELLO_MSG = null;
/**
* Default value for {@link ZMQ#ZMQ_HICCUP_MSG}
*/
public static final Msg DEFAULT_HICCUP_MSG = null;
/**
* Default value for {@link ZMQ#ZMQ_IDENTITY}
*/
public static final byte[] DEFAULT_IDENTITY = new byte[0];
/**
* Default value for {@link ZMQ#ZMQ_IMMEDIATE}
*/
public static final boolean DEFAULT_IMMEDIATE = true;
/**
* Default value for {@link ZMQ#ZMQ_IPV6}
*/
public static final boolean DEFAULT_IPV6 = ZMQ.PREFER_IPV6;
/**
* Default value for {@link ZMQ#ZMQ_LINGER}
*/
public static final int DEFAULT_LINGER = -1;
/**
* Default value for {@link ZMQ#ZMQ_MAXMSGSIZE}
*/
public static final long DEFAULT_MAX_MSG_SIZE = -1;
/**
* Default value for {@link ZMQ#ZMQ_MECHANISM}
*/
public static final Mechanisms DEFAULT_MECHANISM = Mechanisms.NULL;
/**
* Default value for {@link ZMQ#ZMQ_MSG_ALLOCATION_HEAP_THRESHOLD}
*/
public static final int DEFAULT_ALLOCATION_HEAP_THRESHOLD = Config.MSG_ALLOCATION_HEAP_THRESHOLD.getValue();
/**
* Default value for {@link ZMQ#ZMQ_MSG_ALLOCATOR}
*/
public static final MsgAllocator DEFAULT_MSG_ALLOCATOR = new MsgAllocatorThreshold(DEFAULT_ALLOCATION_HEAP_THRESHOLD);
/**
* Default value for {@link ZMQ#ZMQ_RECONNECT_IVL}
*/
public static final int DEFAULT_RECONNECT_IVL = 100;
/**
* Default value for {@link ZMQ#ZMQ_RECONNECT_IVL_MAX}
*/
public static final int DEFAULT_RECONNECT_IVL_MAX = 0;
/**
* Default value for {@link ZMQ#ZMQ_RCVHWM}
*/
public static final int DEFAULT_RECV_HWM = 1000;
/**
* Default value for {@link ZMQ#ZMQ_RCVTIMEO}
*/
public static final int DEFAULT_RECV_TIMEOUT = -1;
/**
* Default value for {@link ZMQ#ZMQ_RCVBUF}
*/
public static final int DEFAULT_RCVBUF = 0;
/**
* Default value for {@link ZMQ#ZMQ_RATE}
*/
public static final int DEFAULT_RATE = 100;
/**
* Default value for {@link ZMQ#ZMQ_RECOVERY_IVL}
*/
public static final int DEFAULT_RECOVERY_IVL = 10000;
/**
* Default value for {@link ZMQ#ZMQ_SELFADDR_PROPERTY_NAME}
*/
public static final String DEFAULT_SELF_ADDRESS_PROPERTY_NAME = null;
/**
* Default value for {@link ZMQ#ZMQ_SNDHWM}
*/
public static final int DEFAULT_SEND_HWM = 1000;
/**
* Default value for {@link ZMQ#ZMQ_SNDTIMEO}
*/
public static final int DEFAULT_SEND_TIMEOUT = -1;
/**
* Default value for {@link ZMQ#ZMQ_SELECTOR_PROVIDERCHOOSER}
*/
public static final SelectorProviderChooser DEFAULT_SELECTOR_CHOOSER = null;
/**
* Default value for {@link ZMQ#ZMQ_SNDBUF}
*/
public static final int DEFAULT_SNDBUF = 0;
/**
* Default value for {@link ZMQ#ZMQ_SOCKS_PROXY}
*/
public static final String DEFAULT_SOCKS_PROXY_ADDRESS = null;
/**
* Default value for {@link ZMQ#ZMQ_TCP_KEEPALIVE}
*/
public static final int DEFAULT_TCP_KEEP_ALIVE = -1;
/**
* Default value for {@link ZMQ#ZMQ_TCP_KEEPALIVE_CNT}
*/
public static final int DEFAULT_TCP_KEEP_ALIVE_CNT = -1;
/**
* Default value for {@link ZMQ#ZMQ_TCP_KEEPALIVE_IDLE}
*/
public static final int DEFAULT_TCP_KEEP_ALIVE_IDLE = -1;
/**
* Default value for {@link ZMQ#ZMQ_TCP_KEEPALIVE_INTVL}
*/
public static final int DEFAULT_TCP_KEEP_ALIVE_INTVL = -1;
/**
* Default value for {@link ZMQ#ZMQ_TOS}
*/
public static final int DEFAULT_TOS = 0;
/**
* Default value for {@link ZMQ#ZMQ_TYPE}
*/
public static final int DEFAULT_TYPE = -1;
/**
* Default value for {@link ZMQ#ZMQ_MULTICAST_HOPS}
*/
public static final int DEFAULT_MULTICAST_HOPS = 1;
/**
* Default value for {@link ZMQ#ZMQ_ZAP_DOMAIN}
*/
public static final String DEFAULT_ZAP_DOMAIN = "";
public static final boolean PREFER_IPV6;
static {
String preferIPv4Stack = System.getProperty("java.net.preferIPv4Stack");
String preferIPv6Addresses = System.getProperty("java.net.preferIPv6Addresses");
PREFER_IPV6 = "false".equalsIgnoreCase(preferIPv4Stack) || "true".equalsIgnoreCase(preferIPv6Addresses);
}
/**
* An interface used to consume events in monitor
*/
public interface EventConsummer
{
void consume(Event ev);
/**
* An optional method to close the monitor if needed
*/
default void close()
{
// Default do nothing
}
}
public static class Event
{
private static final int VALUE_INTEGER = 1;
private static final int VALUE_CHANNEL = 2;
public final int event;
public final String addr;
public final Object arg;
private final int flag;
public Event(int event, String addr, Object arg)
{
this.event = event;
this.addr = addr;
this.arg = arg;
if (arg instanceof Integer) {
flag = VALUE_INTEGER;
}
else if (arg instanceof SelectableChannel) {
flag = VALUE_CHANNEL;
}
else {
flag = 0;
}
}
private Event(int event, String addr, Object arg, int flag)
{
this.event = event;
this.addr = addr;
this.arg = arg;
this.flag = flag;
}
public boolean write(SocketBase s)
{
Msg msg = new Msg(serialize(s.getCtx()));
return s.send(msg, 0);
}
private ByteBuffer serialize(Ctx ctx)
{
int size = 4 + 1 + addr.length() + 1; // event + len(addr) + addr + flag
if (flag == VALUE_INTEGER || flag == VALUE_CHANNEL) {
size += 4;
}
ByteBuffer buffer = ByteBuffer.allocate(size).order(ByteOrder.BIG_ENDIAN);
buffer.putInt(event);
buffer.put((byte) addr.length());
buffer.put(addr.getBytes(CHARSET));
buffer.put((byte) flag);
if (flag == VALUE_INTEGER) {
buffer.putInt((Integer) arg);
}
else if (flag == VALUE_CHANNEL) {
int channeldId = ctx.forwardChannel((SelectableChannel) arg);
buffer.putInt(channeldId);
}
buffer.flip();
return buffer;
}
/**
* Resolve the channel that was associated with this event.
* Implementation note: to be backward compatible, {@link #arg} only store Integer value, so
* the channel is resolved using this call.
*
* Internally socket are kept using weak values, so it's better to retrieve the channel as early
* as possible, otherwise it might get lost.
*
* @param socket the socket that send the event
* @return the channel in the event, or null if was not a channel event.
*/
public SelectableChannel getChannel(SocketBase socket)
{
return getChannel(socket.getCtx());
}
/**
* Resolve the channel that was associated with this event.
* Implementation note: to be backward compatible, {@link #arg} only store Integer value, so
* the channel is resolved using this call.
*
* Internally socket are kept using weak values, so it's better to retrieve the channel as early
* as possible, otherwise it might get lost.
*
* @param ctx the socket that send the event
* @return the channel in the event, or null if was not a channel event.
*/
public SelectableChannel getChannel(Ctx ctx)
{
if (flag == VALUE_CHANNEL) {
return ctx.getForwardedChannel((Integer) arg);
}
else {
return null;
}
}
public static Event read(SocketBase s, int flags)
{
Msg msg = s.recv(flags);
if (msg == null) {
return null;
}
ByteBuffer buffer = msg.buf();
int event = buffer.getInt();
int len = buffer.get();
byte[] addr = new byte[len];
buffer.get(addr);
int flag = buffer.get();
Object arg = null;
if (flag == VALUE_INTEGER || flag == VALUE_CHANNEL) {
arg = buffer.getInt();
}
return new Event(event, new String(addr, CHARSET), arg, flag);
}
public static Event read(SocketBase s)
{
return read(s, 0);
}
}
// New context API
public static Ctx createContext()
{
// Create 0MQ context.
return new Ctx();
}
private static void checkContext(Ctx ctx)
{
if (ctx == null || !ctx.isActive()) {
throw new IllegalStateException();
}
}
private static void destroyContext(Ctx ctx)
{
checkContext(ctx);
ctx.terminate();
}
public static void setContextOption(Ctx ctx, int option, int optval)
{
checkContext(ctx);
ctx.set(option, optval);
}
public static int getContextOption(Ctx ctx, int option)
{
checkContext(ctx);
return ctx.get(option);
}
// Stable/legacy context API
public static Ctx init(int ioThreads)
{
Utils.checkArgument(ioThreads >= 0, "I/O threads must not be negative");
Ctx ctx = createContext();
setContextOption(ctx, ZMQ_IO_THREADS, ioThreads);
return ctx;
}
public static void term(Ctx ctx)
{
destroyContext(ctx);
}
// Sockets
public static SocketBase socket(Ctx ctx, int type)
{
checkContext(ctx);
return ctx.createSocket(type);
}
private static void checkSocket(SocketBase s)
{
if (s == null || !s.isActive()) {
throw new IllegalStateException();
}
}
public static void closeZeroLinger(SocketBase s)
{
checkSocket(s);
s.setSocketOpt(ZMQ.ZMQ_LINGER, 0);
s.close();
}
public static void close(SocketBase s)
{
checkSocket(s);
s.close();
}
public static boolean setSocketOption(SocketBase s, int option, Object optval)
{
checkSocket(s);
return s.setSocketOpt(option, optval);
}
public static Object getSocketOptionExt(SocketBase s, int option)
{
checkSocket(s);
return s.getSocketOptx(option);
}
public static int getSocketOption(SocketBase s, int opt)
{
return s.getSocketOpt(opt);
}
public static boolean monitorSocket(SocketBase s, final String addr, int events)
{
checkSocket(s);
return s.monitor(addr, events);
}
public static boolean bind(SocketBase s, final String addr)
{
checkSocket(s);
return s.bind(addr);
}
public static boolean connect(SocketBase s, String addr)
{
checkSocket(s);
return s.connect(addr);
}
public static int connectPeer(SocketBase s, String addr)
{
checkSocket(s);
return s.connectPeer(addr);
}
public static boolean disconnectPeer(SocketBase s, int routingId)
{
checkSocket(s);
return s.disconnectPeer(routingId);
}
public static boolean unbind(SocketBase s, String addr)
{
checkSocket(s);
return s.termEndpoint(addr);
}
public static boolean disconnect(SocketBase s, String addr)
{
checkSocket(s);
return s.termEndpoint(addr);
}
// Sending functions.
public static int send(SocketBase s, String str, int flags)
{
byte[] data = str.getBytes(CHARSET);
return send(s, data, data.length, flags);
}
public static int send(SocketBase s, Msg msg, int flags)
{
int rc = sendMsg(s, msg, flags);
if (rc < 0) {
return -1;
}
return rc;
}
public static int send(SocketBase s, byte[] buf, int flags)
{
return send(s, buf, buf.length, flags);
}
public static int send(SocketBase s, byte[] buf, int len, int flags)
{
checkSocket(s);
Msg msg = new Msg(len);
msg.put(buf, 0, len);
int rc = sendMsg(s, msg, flags);
if (rc < 0) {
return -1;
}
return rc;
}
// Send multiple messages.
//
// If flag bit ZMQ_SNDMORE is set the vector is treated as
// a single multi-part message, i.e. the last message has
// ZMQ_SNDMORE bit switched off.
//
public int sendiov(SocketBase s, byte[][] a, int count, int flags)
{
checkSocket(s);
int rc = 0;
Msg msg;
for (int i = 0; i < count; ++i) {
msg = new Msg(a[i]);
if (i == count - 1) {
flags = flags & ~ZMQ_SNDMORE;
}
rc = sendMsg(s, msg, flags);
if (rc < 0) {
rc = -1;
break;
}
}
return rc;
}
public static boolean sendMsg(SocketBase socket, byte[]... data)
{
int rc;
if (data.length == 0) {
return false;
}
for (int idx = 0; idx < data.length - 1; ++idx) {
rc = send(socket, new Msg(data[idx]), ZMQ_MORE);
if (rc < 0) {
return false;
}
}
rc = send(socket, new Msg(data[data.length - 1]), 0);
return rc >= 0;
}
public static int sendMsg(SocketBase s, Msg msg, int flags)
{
int sz = msgSize(msg);
boolean rc = s.send(msg, flags);
if (!rc) {
return -1;
}
return sz;
}
// Receiving functions.
public static Msg recv(SocketBase s, int flags)
{
checkSocket(s);
return recvMsg(s, flags);
}
// Receive a multi-part message
//
// Receives up to *count_ parts of a multi-part message.
// Sets *count_ to the actual number of parts read.
// ZMQ_RCVMORE is set to indicate if a complete multi-part message was read.
// Returns number of message parts read, or -1 on error.
//
// Note: even if -1 is returned, some parts of the message
// may have been read. Therefore the client must consult
// *count_ to retrieve message parts successfully read,
// even if -1 is returned.
//
// The iov_base* buffers of each iovec *a_ filled in by this
// function may be freed using free().
//
// Implementation note: We assume zmq::msg_t buffer allocated
// by zmq::recvmsg can be freed by free().
// We assume it is safe to steal these buffers by simply
// not closing the zmq::msg_t.
//
public int recviov(SocketBase s, byte[][] a, int count, int flags)
{
checkSocket(s);
int nread = 0;
boolean recvmore = true;
for (int i = 0; recvmore && i < count; ++i) {
// Cheat! We never close any msg
// because we want to steal the buffer.
Msg msg = recvMsg(s, flags);
if (msg == null) {
nread = -1;
break;
}
// Cheat: acquire zmq_msg buffer.
a[i] = msg.data();
// Assume zmq_socket ZMQ_RVCMORE is properly set.
recvmore = msg.hasMore();
}
return nread;
}
public static Msg recvMsg(SocketBase s, int flags)
{
return s.recv(flags);
}
public static boolean join(SocketBase s, String group)
{
checkSocket(s);
return s.join(group);
}
public static boolean leave(SocketBase s, String group)
{
checkSocket(s);
return s.leave(group);
}
public static Msg msgInit()
{
return new Msg();
}
public static Msg msgInitWithSize(int messageSize)
{
return new Msg(messageSize);
}
public static int msgSize(Msg msg)
{
return msg.size();
}
public static int getMessageOption(Msg msg, int option)
{
switch (option) {
case ZMQ_MORE:
return msg.hasMore() ? 1 : 0;
default:
throw new IllegalArgumentException();
}
}
// Get message metadata string
public static String getMessageMetadata(Msg msg, String property)
{
String data = null;
Metadata metadata = msg.getMetadata();
if (metadata != null) {
data = metadata.get(property);
}
return data;
}
// Set routing id on a message sent over SERVER socket type
public boolean setMessageRoutingId(Msg msg, int routingId)
{
return msg.setRoutingId(routingId);
}
// Get the routing id of a message that came from SERVER socket type
public int getMessageRoutingId(Msg msg)
{
return msg.getRoutingId();
}
public boolean setMessageGroup(Msg msg, String group)
{
return msg.setGroup(group);
}
public String getMessageGroup(Msg msg)
{
return msg.getGroup();
}
public static void sleep(long seconds)
{
sleep(seconds, TimeUnit.SECONDS);
}
public static void msleep(long milliseconds)
{
sleep(milliseconds, TimeUnit.MILLISECONDS);
}
public static void sleep(long amount, TimeUnit unit)
{
LockSupport.parkNanos(TimeUnit.NANOSECONDS.convert(amount, unit));
}
/**
* Polling on items with given selector
* CAUTION: This could be affected by jdk epoll bug
*
* @param selector Open and reuse this selector and do not forget to close when it is not used.
* @param items
* @param timeout
* @return number of events
*/
public static int poll(Selector selector, PollItem[] items, long timeout)
{
return poll(selector, items, items.length, timeout);
}
/**
* Polling on items with given selector
* CAUTION: This could be affected by jdk epoll bug
*
* @param selector Open and reuse this selector and do not forget to close when it is not used.
* @param items
* @param count
* @param timeout
* @return number of events
*/
public static int poll(Selector selector, PollItem[] items, int count, long timeout)
{
Utils.checkArgument(items != null, "items have to be supplied for polling");
if (count == 0) {
if (timeout <= 0) {
return 0;
}
LockSupport.parkNanos(TimeUnit.NANOSECONDS.convert(timeout, TimeUnit.MILLISECONDS));
return 0;
}
long now = 0L;
long end = 0L;
HashMap saved = new HashMap<>();
for (SelectionKey key : selector.keys()) {
if (key.isValid()) {
saved.put(key.channel(), key);
}
}
for (int i = 0; i < count; i++) {
PollItem item = items[i];
if (item == null) {
continue;
}
SelectableChannel ch = item.getChannel(); // mailbox channel if ZMQ socket
SelectionKey key = saved.remove(ch);
if (key != null) {
if (key.interestOps() != item.interestOps()) {
key.interestOps(item.interestOps());
}
key.attach(item);
}
else {
try {
ch.register(selector, item.interestOps(), item);
}
catch (ClosedSelectorException e) {
// context was closed asynchronously, exit gracefully
return -1;
}
catch (ClosedChannelException e) {
throw new ZError.IOException(e);
}
}
}
if (!saved.isEmpty()) {
for (SelectionKey deprecated : saved.values()) {
deprecated.cancel();
}
}
boolean firstPass = true;
int nevents = 0;
int ready;
while (true) {
// Compute the timeout for the subsequent poll.
long waitMillis;
if (firstPass) {
waitMillis = 0L;
}
else if (timeout < 0L) {
waitMillis = -1L;
}
else {
waitMillis = TimeUnit.NANOSECONDS.toMillis(end - now);
if (waitMillis == 0) {
waitMillis = 1L;
}
}
// Wait for events.
try {
int rc;
if (waitMillis < 0) {
rc = selector.select(0);
}
else if (waitMillis == 0) {
rc = selector.selectNow();
}
else {
rc = selector.select(waitMillis);
}
for (SelectionKey key : selector.keys()) {
PollItem item = (PollItem) key.attachment();
ready = item.readyOps(key, rc);
if (ready < 0) {
return -1;
}
if (ready > 0) {
nevents++;
}
}
selector.selectedKeys().clear();
}
catch (ClosedSelectorException e) {
// context was closed asynchronously, exit gracefully
return -1;
}
catch (IOException e) {
throw new ZError.IOException(e);
}
// If timeout is zero, exit immediately whether there are events or not.
if (timeout == 0) {
break;
}
if (nevents > 0) {
break;
}
// At this point we are meant to wait for events but there are none.
// If timeout is infinite we can just loop until we get some events.
if (timeout < 0) {
if (firstPass) {
firstPass = false;
}
continue;
}
// The timeout is finite and there are no events. In the first pass
// we get a timestamp of when the polling have begun. (We assume that
// first pass have taken negligible time). We also compute the time
// when the polling should time out.
if (firstPass) {
now = Clock.nowNS();
end = now + TimeUnit.MILLISECONDS.toNanos(timeout);
if (now == end) {
break;
}
firstPass = false;
continue;
}
// Find out whether timeout have expired.
now = Clock.nowNS();
if (now >= end) {
break;
}
}
return nevents;
}
// The proxy functionality
public static boolean proxy(SocketBase frontend, SocketBase backend, SocketBase capture)
{
Utils.checkArgument(frontend != null, "Frontend socket has to be present for proxy");
Utils.checkArgument(backend != null, "Backend socket has to be present for proxy");
return Proxy.proxy(frontend, backend, capture, null);
}
public static boolean proxy(SocketBase frontend, SocketBase backend, SocketBase capture, SocketBase control)
{
Utils.checkArgument(frontend != null, "Frontend socket has to be present for proxy");
Utils.checkArgument(backend != null, "Backend socket has to be present for proxy");
return Proxy.proxy(frontend, backend, capture, control);
}
public static boolean device(int device, SocketBase frontend, SocketBase backend)
{
Utils.checkArgument(frontend != null, "Frontend socket has to be present for proxy");
Utils.checkArgument(backend != null, "Backend socket has to be present for proxy");
return Proxy.proxy(frontend, backend, null, null);
}
public static long startStopwatch()
{
return System.nanoTime();
}
public static long stopStopwatch(long watch)
{
return (System.nanoTime() - watch) / 1000;
}
public static int makeVersion(int major, int minor, int patch)
{
return ((major) * 10000 + (minor) * 100 + (patch));
}
public static String strerror(int errno)
{
return "Errno = " + errno;
}
}
jeromq-0.6.0/src/main/java/zmq/ZObject.java 0000664 0000000 0000000 00000023120 14557711263 0020471 0 ustar 00root root 0000000 0000000 package zmq;
import zmq.io.IEngine;
import zmq.io.IOThread;
import zmq.io.SessionBase;
import zmq.pipe.Pipe;
import zmq.pipe.YPipeBase;
// Base class for all objects that participate in inter-thread
// communication.
public abstract class ZObject
{
// Context provides access to the global state.
private final Ctx ctx;
// Thread ID of the thread the object belongs to.
private int tid;
protected ZObject(Ctx ctx, int tid)
{
this.ctx = ctx;
this.tid = tid;
}
protected ZObject(ZObject parent)
{
this(parent.ctx, parent.tid);
}
public final int getTid()
{
return tid;
}
protected final void setTid(int tid)
{
this.tid = tid;
}
public final Ctx getCtx()
{
return ctx;
}
@SuppressWarnings("unchecked")
final void processCommand(Command cmd)
{
// System.out.println(Thread.currentThread().getName() + ": Processing command " + cmd);
switch (cmd.type) {
case ACTIVATE_READ:
processActivateRead();
break;
case ACTIVATE_WRITE:
processActivateWrite((Long) cmd.arg);
break;
case STOP:
processStop();
break;
case PLUG:
processPlug();
processSeqnum();
break;
case OWN:
processOwn((Own) cmd.arg);
processSeqnum();
break;
case ATTACH:
processAttach((IEngine) cmd.arg);
processSeqnum();
break;
case BIND:
processBind((Pipe) cmd.arg);
processSeqnum();
break;
case HICCUP:
processHiccup((YPipeBase) cmd.arg);
break;
case PIPE_TERM:
processPipeTerm();
break;
case PIPE_TERM_ACK:
processPipeTermAck();
break;
case TERM_REQ:
processTermReq((Own) cmd.arg);
break;
case TERM:
processTerm((Integer) cmd.arg);
break;
case TERM_ACK:
processTermAck();
break;
case REAP:
processReap((SocketBase) cmd.arg);
break;
case REAP_ACK:
processReapAck();
break;
case REAPED:
processReaped();
break;
case INPROC_CONNECTED:
processSeqnum();
break;
case CANCEL:
processCancel();
break;
case DONE:
default:
throw new IllegalArgumentException();
}
}
protected final boolean registerEndpoint(String addr, Ctx.Endpoint endpoint)
{
return ctx.registerEndpoint(addr, endpoint);
}
protected final boolean unregisterEndpoint(String addr, SocketBase socket)
{
return ctx.unregisterEndpoint(addr, socket);
}
protected final void unregisterEndpoints(SocketBase socket)
{
ctx.unregisterEndpoints(socket);
}
protected final Ctx.Endpoint findEndpoint(String addr)
{
return ctx.findEndpoint(addr);
}
protected final void pendConnection(String addr, Ctx.Endpoint endpoint, Pipe[] pipes)
{
ctx.pendConnection(addr, endpoint, pipes);
}
protected final void connectPending(String addr, SocketBase bindSocket)
{
ctx.connectPending(addr, bindSocket);
}
protected final void destroySocket(SocketBase socket)
{
ctx.destroySocket(socket);
}
// Chooses least loaded I/O thread.
protected final IOThread chooseIoThread(long affinity)
{
return ctx.chooseIoThread(affinity);
}
protected final void sendStop()
{
// 'stop' command goes always from administrative thread to
// the current object.
Command cmd = new Command(this, Command.Type.STOP);
ctx.sendCommand(tid, cmd);
}
protected final void sendPlug(Own destination)
{
sendPlug(destination, true);
}
protected final void sendPlug(Own destination, boolean incSeqnum)
{
if (incSeqnum) {
destination.incSeqnum();
}
Command cmd = new Command(destination, Command.Type.PLUG);
sendCommand(cmd);
}
protected final void sendOwn(Own destination, Own object)
{
destination.incSeqnum();
Command cmd = new Command(destination, Command.Type.OWN, object);
sendCommand(cmd);
}
protected final void sendAttach(SessionBase destination, IEngine engine)
{
sendAttach(destination, engine, true);
}
protected final void sendAttach(SessionBase destination, IEngine engine, boolean incSeqnum)
{
if (incSeqnum) {
destination.incSeqnum();
}
Command cmd = new Command(destination, Command.Type.ATTACH, engine);
sendCommand(cmd);
}
protected final void sendBind(Own destination, Pipe pipe)
{
sendBind(destination, pipe, true);
}
protected final void sendBind(Own destination, Pipe pipe, boolean incSeqnum)
{
if (incSeqnum) {
destination.incSeqnum();
}
Command cmd = new Command(destination, Command.Type.BIND, pipe);
sendCommand(cmd);
}
protected final void sendActivateRead(Pipe destination)
{
Command cmd = new Command(destination, Command.Type.ACTIVATE_READ);
sendCommand(cmd);
}
protected final void sendActivateWrite(Pipe destination, long msgsRead)
{
Command cmd = new Command(destination, Command.Type.ACTIVATE_WRITE, msgsRead);
sendCommand(cmd);
}
protected final void sendHiccup(Pipe destination, YPipeBase pipe)
{
Command cmd = new Command(destination, Command.Type.HICCUP, pipe);
sendCommand(cmd);
}
protected final void sendPipeTerm(Pipe destination)
{
Command cmd = new Command(destination, Command.Type.PIPE_TERM);
sendCommand(cmd);
}
protected final void sendPipeTermAck(Pipe destination)
{
Command cmd = new Command(destination, Command.Type.PIPE_TERM_ACK);
sendCommand(cmd);
}
protected final void sendTermReq(Own destination, Own object)
{
Command cmd = new Command(destination, Command.Type.TERM_REQ, object);
sendCommand(cmd);
}
protected final void sendTerm(Own destination, int linger)
{
Command cmd = new Command(destination, Command.Type.TERM, linger);
sendCommand(cmd);
}
protected final void sendTermAck(Own destination)
{
Command cmd = new Command(destination, Command.Type.TERM_ACK);
sendCommand(cmd);
}
protected final void sendReap(SocketBase socket)
{
Command cmd = new Command(ctx.getReaper(), Command.Type.REAP, socket);
sendCommand(cmd);
}
protected final void sendReapAck()
{
Command cmd = new Command(this, Command.Type.REAP_ACK);
sendCommand(cmd);
}
protected final void sendReaped()
{
Command cmd = new Command(ctx.getReaper(), Command.Type.REAPED);
sendCommand(cmd);
}
protected final void sendInprocConnected(SocketBase socket)
{
Command cmd = new Command(socket, Command.Type.INPROC_CONNECTED);
sendCommand(cmd);
}
protected final void sendDone()
{
Command cmd = new Command(null, Command.Type.DONE);
ctx.sendCommand(Ctx.TERM_TID, cmd);
}
protected final void sendCancel()
{
Command cmd = new Command(this, Command.Type.CANCEL);
sendCommand(cmd);
}
protected void processStop()
{
throw new UnsupportedOperationException();
}
protected void processPlug()
{
throw new UnsupportedOperationException();
}
protected void processOwn(Own object)
{
throw new UnsupportedOperationException();
}
protected void processAttach(IEngine engine)
{
throw new UnsupportedOperationException();
}
protected void processBind(Pipe pipe)
{
throw new UnsupportedOperationException();
}
protected void processActivateRead()
{
throw new UnsupportedOperationException();
}
protected void processActivateWrite(long msgsRead)
{
throw new UnsupportedOperationException();
}
protected void processHiccup(YPipeBase hiccupPipe)
{
throw new UnsupportedOperationException();
}
protected void processPipeTerm()
{
throw new UnsupportedOperationException();
}
protected void processPipeTermAck()
{
throw new UnsupportedOperationException();
}
protected void processTermReq(Own object)
{
throw new UnsupportedOperationException();
}
protected void processTerm(int linger)
{
throw new UnsupportedOperationException();
}
protected void processTermAck()
{
throw new UnsupportedOperationException();
}
protected void processReap(SocketBase socket)
{
throw new UnsupportedOperationException();
}
protected void processReapAck()
{
}
protected void processReaped()
{
throw new UnsupportedOperationException();
}
// Special handler called after a command that requires a seqnum
// was processed. The implementation should catch up with its counter
// of processed commands here.
protected void processSeqnum()
{
throw new UnsupportedOperationException();
}
protected void processCancel()
{
}
private void sendCommand(Command cmd)
{
ctx.sendCommand(cmd.destination.getTid(), cmd);
}
}
jeromq-0.6.0/src/main/java/zmq/io/ 0000775 0000000 0000000 00000000000 14557711263 0016677 5 ustar 00root root 0000000 0000000 jeromq-0.6.0/src/main/java/zmq/io/EngineNotImplemented.java 0000664 0000000 0000000 00000001150 14557711263 0023611 0 ustar 00root root 0000000 0000000 package zmq.io;
public class EngineNotImplemented implements IEngine
{
public EngineNotImplemented()
{
throw new UnsupportedOperationException(getClass().getName() + " is not implemented");
}
@Override
public void plug(IOThread ioThread, SessionBase session)
{
}
@Override
public void terminate()
{
}
@Override
public void restartInput()
{
}
@Override
public void restartOutput()
{
}
@Override
public void zapMsgAvailable()
{
}
@Override
public String getEndPoint()
{
return null;
}
}
jeromq-0.6.0/src/main/java/zmq/io/HelloMsgSession.java 0000664 0000000 0000000 00000001241 14557711263 0022616 0 ustar 00root root 0000000 0000000 package zmq.io;
import zmq.Msg;
import zmq.Options;
import zmq.SocketBase;
import zmq.io.net.Address;
public class HelloMsgSession extends SessionBase
{
boolean newPipe;
public HelloMsgSession(IOThread ioThread, boolean connect, SocketBase socket, Options options, Address addr)
{
super(ioThread, connect, socket, options, addr);
newPipe = true;
}
@Override
protected Msg pullMsg()
{
if (newPipe) {
newPipe = false;
return options.helloMsg;
}
return super.pullMsg();
}
@Override
protected void reset()
{
super.reset();
newPipe = true;
}
}
jeromq-0.6.0/src/main/java/zmq/io/IEngine.java 0000664 0000000 0000000 00000001225 14557711263 0021060 0 ustar 00root root 0000000 0000000 package zmq.io;
// Abstract interface to be implemented by various engines.
public interface IEngine
{
// Plug the engine to the session.
void plug(IOThread ioThread, SessionBase session);
// Terminate and deallocate the engine. Note that 'detached'
// events are not fired on termination.
void terminate();
// This method is called by the session to signal that more
// messages can be written to the pipe.
void restartInput();
// This method is called by the session to signal that there
// are messages to send available.
void restartOutput();
void zapMsgAvailable();
String getEndPoint();
}
jeromq-0.6.0/src/main/java/zmq/io/IOObject.java 0000664 0000000 0000000 00000005312 14557711263 0021201 0 ustar 00root root 0000000 0000000 package zmq.io;
import java.nio.channels.SelectableChannel;
import zmq.poll.IPollEvents;
import zmq.poll.Poller;
import zmq.poll.Poller.Handle;
// Simple base class for objects that live in I/O threads.
// It makes communication with the poller object easier and
// makes defining unneeded event handlers unnecessary.
public class IOObject implements IPollEvents
{
private final Poller poller;
private final IPollEvents handler;
private boolean alive;
public IOObject(IOThread ioThread, IPollEvents handler)
{
assert (ioThread != null);
assert (handler != null);
this.handler = handler;
// Retrieve the poller from the thread we are running in.
poller = ioThread.getPoller();
}
// When migrating an object from one I/O thread to another, first
// unplug it, then migrate it, then plug it to the new thread.
public final void plug()
{
alive = true;
}
public final void unplug()
{
alive = false;
}
public final Handle addFd(SelectableChannel fd)
{
return poller.addHandle(fd, this);
}
public final void removeHandle(Handle handle)
{
poller.removeHandle(handle);
}
public final void setPollIn(Handle handle)
{
poller.setPollIn(handle);
}
public final void setPollOut(Handle handle)
{
poller.setPollOut(handle);
}
public final void setPollConnect(Handle handle)
{
poller.setPollConnect(handle);
}
public final void setPollAccept(Handle handle)
{
poller.setPollAccept(handle);
}
public final void resetPollIn(Handle handle)
{
poller.resetPollIn(handle);
}
public final void resetPollOut(Handle handle)
{
poller.resetPollOut(handle);
}
@Override
public final void inEvent()
{
assert (alive);
handler.inEvent();
}
@Override
public final void outEvent()
{
assert (alive);
handler.outEvent();
}
@Override
public final void connectEvent()
{
assert (alive);
handler.connectEvent();
}
@Override
public final void acceptEvent()
{
assert (alive);
handler.acceptEvent();
}
@Override
public final void timerEvent(int id)
{
assert (alive);
handler.timerEvent(id);
}
public final void addTimer(long timeout, int id)
{
assert (alive);
poller.addTimer(timeout, this, id);
}
public final void cancelTimer(int id)
{
assert (alive);
poller.cancelTimer(this, id);
}
@Override
public String toString()
{
return String.valueOf(handler);
}
}
jeromq-0.6.0/src/main/java/zmq/io/IOThread.java 0000664 0000000 0000000 00000003716 14557711263 0021210 0 ustar 00root root 0000000 0000000 package zmq.io;
import java.io.Closeable;
import java.io.IOException;
import java.nio.channels.SelectableChannel;
import zmq.Command;
import zmq.Ctx;
import zmq.Mailbox;
import zmq.ZObject;
import zmq.poll.IPollEvents;
import zmq.poll.Poller;
public class IOThread extends ZObject implements IPollEvents, Closeable
{
// I/O thread accesses incoming commands via this mailbox.
private final Mailbox mailbox;
// Handle associated with mailbox' file descriptor.
private final Poller.Handle mailboxHandle;
// I/O multiplexing is performed using a poller object.
private final Poller poller;
public IOThread(Ctx ctx, int tid)
{
super(ctx, tid);
String name = "iothread-" + tid;
poller = new Poller(ctx, name);
mailbox = new Mailbox(ctx, name, tid);
SelectableChannel fd = mailbox.getFd();
mailboxHandle = poller.addHandle(fd, this);
poller.setPollIn(mailboxHandle);
}
public void start()
{
poller.start();
}
@Override
public void close() throws IOException
{
poller.destroy();
mailbox.close();
}
public void stop()
{
sendStop();
}
public Mailbox getMailbox()
{
return mailbox;
}
public int getLoad()
{
return poller.getLoad();
}
@Override
public void inEvent()
{
// TODO: Do we want to limit number of commands I/O thread can
// process in a single go?
while (true) {
// Get the next command. If there is none, exit.
Command cmd = mailbox.recv(0);
if (cmd == null) {
break;
}
// Process the command.
cmd.process();
}
}
Poller getPoller()
{
assert (poller != null);
return poller;
}
@Override
protected void processStop()
{
poller.removeHandle(mailboxHandle);
poller.stop();
}
}
jeromq-0.6.0/src/main/java/zmq/io/Metadata.java 0000664 0000000 0000000 00000033102 14557711263 0021261 0 ustar 00root root 0000000 0000000 package zmq.io;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import zmq.Msg;
import zmq.ZError;
import zmq.ZMQ;
import zmq.util.Wire;
/**
* The metadata holder class.
* The class is thread safe, as it uses a {@link ConcurrentHashMap} for the backend
* Null value are transformed to empty string.
*/
public class Metadata
{
/**
* Call backs during parsing process
*/
public interface ParseListener
{
/**
* Called when a property has been parsed.
* @param name the name of the property.
* @param value the value of the property.
* @param valueAsString the value in a string representation.
* @return 0 to continue the parsing process, any other value to interrupt it.
*/
int parsed(String name, byte[] value, String valueAsString);
}
public static final String IDENTITY = "Identity";
public static final String SOCKET_TYPE = "Socket-Type";
public static final String USER_ID = "User-Id";
public static final String PEER_ADDRESS = "Peer-Address";
// Dictionary holding metadata.
private final Map dictionary = new ConcurrentHashMap<>();
public Metadata()
{
super();
}
public Metadata(Properties dictionary)
{
// Ensure a conversion of each entry to a String.
// No need to check for nul values as the Properties class ignore null and don't stores them
dictionary.forEach((key, value) -> this.dictionary.put(key.toString(), value.toString()));
}
public Metadata(Map dictionary)
{
dictionary.forEach((key, value) -> this.dictionary.put(key, Optional.ofNullable(value).orElse("")));
}
/**
* Returns a {@link Set} view of the keys contained in this metadata.
* The set is backed by the metadata, so changes to the map are
* reflected in the set, and vice-versa. If the metadata is modified
* while an iteration over the set is in progress (except through
* the iterator's own {@code remove} operation), the results of
* the iteration are undefined. The set supports element removal,
* which removes the corresponding mapping from the metadata, via the
* {@code Iterator.remove}, {@code Set.remove},
* {@code removeAll}, {@code retainAll}, and {@code clear}
* operations. It does not support the {@code add} or {@code addAll}
* operations.
*
* @return a set view of the keys contained in this metadata
*/
public Set keySet()
{
return dictionary.keySet();
}
/**
* Returns a {@link Set} view of the properties contained in this metadata.
* The set is backed by the metadata, so changes to the metadata are
* reflected in the set, and vice-versa. If the metadata is modified
* while an iteration over the set is in progress (except through
* the iterator's own {@code remove} operation, or through the
* {@code setValue} operation on a metadata property returned by the
* iterator) the results of the iteration are undefined. The set
* supports element removal, which removes the corresponding
* mapping from the metadata, via the {@code Iterator.remove},
* {@code Set.remove}, {@code removeAll}, {@code retainAll} and
* {@code clear} operations. It does not support the
* {@code add} or {@code addAll} operations.
*
* @return a set view of the properties contained in this metadata
*/
public Set> entrySet()
{
return dictionary.entrySet();
}
/**
* Returns a {@link Collection} view of the values contained in this metadata.
* The collection is backed by the metadata, so changes to the map are
* reflected in the collection, and vice-versa. If the metadata is
* modified while an iteration over the collection is in progress
* (except through the iterator's own {@code remove} operation),
* the results of the iteration are undefined. The collection
* supports element removal, which removes the corresponding
* property from the metadata, via the {@code Iterator.remove},
* {@code Collection.remove}, {@code removeAll},
* {@code retainAll} and {@code clear} operations. It does not
* support the {@code add} or {@code addAll} operations.
*
* @return a collection view of the values contained in this map
*/
public Collection values()
{
return dictionary.values();
}
/**
* Removes the property for a name from this metada if it is present.
*
* If this map permits null values, then a return value of
* {@code null} does not necessarily indicate that the map
* contained no mapping for the key; it's also possible that the map
* explicitly mapped the key to {@code null}.
*
*
The map will not contain a mapping for the specified key once the
* call returns.
*
* @param key key whose mapping is to be removed from the map
*/
public void remove(String key)
{
dictionary.remove(key);
}
/**
* Returns the value for this property,
* or {@code null} if it does not exist.
*
* @param property the property name
* @return the value of the property, or {@code null} if it does not exist.
*/
public String get(String property)
{
return dictionary.get(property);
}
/**
* Define the value for this property. If the value is null, an empty string
* is stored instead.
*
* @param property the property name
* @param value value to be associated with the specified property
* @deprecated Use {@link #put(String, String)} instead
*/
@Deprecated
public void set(String property, String value)
{
put(property, value);
}
/**
* Define the value for this property. If the value is null, an empty string
* is stored instead.
*
* @param property the property name
* @param value value to be associated with the specified property
*/
public void put(String property, String value)
{
if (value != null) {
dictionary.put(property, value);
}
else {
dictionary.put(property, "");
}
}
@Override
public int hashCode()
{
return dictionary.hashCode();
}
@Override
public boolean equals(Object other)
{
if (this == other) {
return true;
}
if (other == null) {
return false;
}
if (!(other instanceof Metadata)) {
return false;
}
Metadata that = (Metadata) other;
return this.dictionary.equals(that.dictionary);
}
public void set(Metadata zapProperties)
{
dictionary.putAll(zapProperties.dictionary);
}
/**
* Returns {@code true} if this map contains no key-value mappings.
*
* @return {@code true} if this map contains no key-value mappings
*/
public boolean isEmpty()
{
return dictionary.isEmpty();
}
/**
* Returns {@code true} if this metada contains the property requested
*
* @param property property the name of the property to be tested.
* @return {@code true} if this metada contains the property
*/
public boolean containsKey(String property)
{
return dictionary.containsKey(property);
}
/**
* Removes all the properties.
* The map will be empty after this call returns.
*/
public void clear()
{
dictionary.clear();
}
/**
* Returns the number of properties. If it contains more properties
* than {@code Integer.MAX_VALUE} elements, returns {@code Integer.MAX_VALUE}.
*
* @return the number of properties
*/
public int size()
{
return dictionary.size();
}
@Override
public String toString()
{
return "Metadata=" + dictionary;
}
/**
* Return the content of the metadata as a new byte array, using the specifications of the ZMTP protocol
*
* property = name value
* name = OCTET 1*255name-char
* name-char = ALPHA | DIGIT | "-" | "_" | "." | "+"
* value = 4OCTET *OCTET ; Size in network byte order
*
* @return a new byte array
* @throws IllegalStateException if the content can't be serialized
*/
public byte[] bytes()
{
try (ByteArrayOutputStream stream = new ByteArrayOutputStream(bytesSize())) {
write(stream);
return stream.toByteArray();
}
catch (IOException e) {
throw new IllegalStateException("Unable to write content as bytes", e);
}
}
/**
* Return an approximate size of the serialization of the metadata, it will probably be higher if there is a lot
* of non ASCII value in it.
* @return the size estimation
*/
private int bytesSize()
{
int size = 0;
for (Entry entry : dictionary.entrySet()) {
size += 1;
size += entry.getKey().length();
size += 4;
size += entry.getValue().length();
}
return size;
}
/**
* Serialize metadata to an output stream, using the specifications of the ZMTP protocol
*
* property = name value
* name = OCTET 1*255name-char
* name-char = ALPHA | DIGIT | "-" | "_" | "." | "+"
* value = 4OCTET *OCTET ; Size in network byte order
*
* @param stream
* @throws IOException if an I/O error occurs.
* @throws IllegalStateException if one of the properties name size is bigger than 255
*/
public void write(OutputStream stream) throws IOException
{
for (Entry entry : dictionary.entrySet()) {
byte[] keyBytes = entry.getKey().getBytes(ZMQ.CHARSET);
if (keyBytes.length > 255) {
throw new IllegalStateException("Trying to serialize an oversize attribute name");
}
// write the length as a byte
stream.write(keyBytes.length);
stream.write(keyBytes);
byte[] valueBytes = entry.getValue().getBytes(ZMQ.CHARSET);
stream.write(Wire.putUInt32(valueBytes.length));
stream.write(valueBytes);
}
}
/**
* Deserialize metadata from a {@link Msg}, using the specifications of the ZMTP protocol
*
* property = name value
* name = OCTET 1*255name-char
* name-char = ALPHA | DIGIT | "-" | "_" | "." | "+"
* value = 4OCTET *OCTET ; Size in network byte order
*
* @param msg
* @param offset
* @param listener an optional {@link ParseListener}, can be null.
* @return 0 if successful. Otherwise, it returns {@code zmq.ZError.EPROTO} or the error value from the {@link ParseListener}.
*/
public int read(Msg msg, int offset, ParseListener listener)
{
return read(msg.buf(), offset, listener);
}
/**
* Deserialize metadata from a {@link ByteBuffer}, using the specifications of the ZMTP protocol
*
* property = name value
* name = OCTET 1*255name-char
* name-char = ALPHA | DIGIT | "-" | "_" | "." | "+"
* value = 4OCTET *OCTET ; Size in network byte order
*
* @param msg
* @param offset
* @param listener an optional {@link ParseListener}, can be null.
* @return 0 if successful. Otherwise, it returns {@code zmq.ZError.EPROTO} or the error value from the {@link ParseListener}.
*/
public int read(ByteBuffer msg, int offset, ParseListener listener)
{
ByteBuffer data = msg.duplicate();
data.position(offset);
int bytesLeft = data.remaining();
int index = offset;
while (bytesLeft > 1) {
final int nameLength = Byte.toUnsignedInt(data.get(index));
if (nameLength == 0) {
break;
}
index++;
bytesLeft -= 1;
if (bytesLeft < nameLength) {
break;
}
final String name = new String(bytes(data, index, nameLength), ZMQ.CHARSET);
index += nameLength;
bytesLeft -= nameLength;
if (bytesLeft < 4) {
break;
}
final int valueLength = Wire.getUInt32(data, index);
index += 4;
bytesLeft -= 4;
if (bytesLeft < valueLength || valueLength < 0) {
break;
}
final byte[] value = bytes(data, index, valueLength);
final String valueAsString = new String(value, ZMQ.CHARSET);
index += valueLength;
bytesLeft -= valueLength;
if (listener != null) {
int rc = listener.parsed(name, value, valueAsString);
if (rc != 0) {
return rc;
}
}
set(name, valueAsString);
}
if (bytesLeft > 0) {
return ZError.EPROTO;
}
return 0;
}
private byte[] bytes(final ByteBuffer buf, final int position, final int length)
{
final byte[] bytes = new byte[length];
final int current = buf.position();
buf.position(position);
buf.get(bytes, 0, length);
buf.position(current);
return bytes;
}
}
jeromq-0.6.0/src/main/java/zmq/io/Msgs.java 0000664 0000000 0000000 00000002240 14557711263 0020451 0 ustar 00root root 0000000 0000000 package zmq.io;
import zmq.Msg;
public class Msgs
{
private Msgs()
{
// no possible instantiation
}
/**
* Checks if the message starts with the given string.
*
* @param msg the message to check.
* @param data the string to check the message with. Shall be shorter than 256 characters.
* @param includeLength true if the string in the message is prefixed with the length, false if not.
* @return true if the message starts with the given string, otherwise false.
*/
public static boolean startsWith(Msg msg, String data, boolean includeLength)
{
final int length = data.length();
assert (length < 256);
int start = includeLength ? 1 : 0;
if (msg.size() < length + start) {
return false;
}
boolean comparison = !includeLength || length == (msg.get(0) & 0xff);
if (comparison) {
for (int idx = start; idx < length; ++idx) {
comparison = (msg.get(idx) == data.charAt(idx - start));
if (!comparison) {
break;
}
}
}
return comparison;
}
}
jeromq-0.6.0/src/main/java/zmq/io/SessionBase.java 0000664 0000000 0000000 00000044211 14557711263 0021762 0 ustar 00root root 0000000 0000000 package zmq.io;
import java.util.HashSet;
import java.util.Set;
import zmq.Ctx;
import zmq.Msg;
import zmq.Options;
import zmq.Own;
import zmq.SocketBase;
import zmq.ZError;
import zmq.ZMQ;
import zmq.ZObject;
import zmq.io.StreamEngine.ErrorReason;
import zmq.io.mechanism.Mechanisms;
import zmq.io.net.Address;
import zmq.io.net.NetProtocol;
import zmq.io.net.ipc.IpcConnecter;
import zmq.io.net.norm.NormEngine;
import zmq.io.net.pgm.PgmReceiver;
import zmq.io.net.pgm.PgmSender;
import zmq.io.net.tcp.SocksConnecter;
import zmq.io.net.tcp.TcpConnecter;
import zmq.io.net.tipc.TipcConnecter;
import zmq.pipe.Pipe;
import zmq.poll.IPollEvents;
public class SessionBase extends Own implements Pipe.IPipeEvents, IPollEvents
{
// If true, this session (re)connects to the peer. Otherwise, it's
// a transient session created by the listener.
private final boolean active;
// Pipe connecting the session to its socket.
private Pipe pipe;
// Pipe used to exchange messages with ZAP socket.
private Pipe zapPipe;
// This set is added to with pipes we are disconnecting, but haven't yet completed
private final Set terminatingPipes;
// This flag is true if the remainder of the message being processed
// is still in the in pipe.
private boolean incompleteIn;
// True if termination have been suspended to push the pending
// messages to the network.
private boolean pending;
// The protocol I/O engine connected to the session.
private IEngine engine;
// The socket the session belongs to.
protected final SocketBase socket;
// I/O thread the session is living in. It will be used to plug in
// the engines into the same thread.
private final IOThread ioThread;
// ID of the linger timer
private static final int LINGER_TIMER_ID = 0x20;
// True is linger timer is running.
private boolean hasLingerTimer;
// Protocol and address to use when connecting.
private final Address addr;
private final IOObject ioObject;
public SessionBase(IOThread ioThread, boolean connect, SocketBase socket, Options options, Address addr)
{
super(ioThread, options);
ioObject = new IOObject(ioThread, this);
this.active = connect;
pipe = null;
zapPipe = null;
incompleteIn = false;
pending = false;
engine = null;
this.socket = socket;
this.ioThread = ioThread;
hasLingerTimer = false;
this.addr = addr;
terminatingPipes = new HashSet<>();
}
@Override
public void destroy()
{
assert (pipe == null);
assert (zapPipe == null);
// If there's still a pending linger timer, remove it.
if (hasLingerTimer) {
ioObject.cancelTimer(LINGER_TIMER_ID);
hasLingerTimer = false;
}
// Close the engine.
if (engine != null) {
engine.terminate();
}
ioObject.unplug();
}
// To be used once only, when creating the session.
public void attachPipe(Pipe pipe)
{
assert (!isTerminating());
assert (this.pipe == null);
assert (pipe != null);
this.pipe = pipe;
this.pipe.setEventSink(this);
}
protected Msg pullMsg()
{
if (pipe == null) {
return null;
}
Msg msg = pipe.read();
if (msg == null) {
return null;
}
incompleteIn = msg.hasMore();
return msg;
}
protected boolean pushMsg(Msg msg)
{
if (msg.isCommand()) {
return true;
}
if (pipe != null && pipe.write(msg)) {
return true;
}
errno.set(ZError.EAGAIN);
return false;
}
public Msg readZapMsg()
{
if (zapPipe == null) {
errno.set(ZError.ENOTCONN);
return null;
}
Msg msg = zapPipe.read();
if (msg == null) {
errno.set(ZError.EAGAIN);
}
return msg;
}
public boolean writeZapMsg(Msg msg)
{
if (zapPipe == null) {
errno.set(ZError.ENOTCONN);
return false;
}
boolean rc = zapPipe.write(msg);
assert (rc);
if (!msg.hasMore()) {
zapPipe.flush();
}
return true;
}
protected void reset()
{
}
public void flush()
{
if (pipe != null) {
pipe.flush();
}
}
// Remove any half processed messages. Flush unflushed messages.
// Call this function when engine disconnect to get rid of leftovers.
private void cleanPipes()
{
assert (pipe != null);
// Get rid of half-processed messages in the out pipe. Flush any
// unflushed messages upstream.
pipe.rollback();
pipe.flush();
// Remove any half-read message from the in pipe.
while (incompleteIn) {
Msg msg = pullMsg();
if (msg == null) {
assert (!incompleteIn);
break;
}
// msg.close ();
}
}
@Override
public void pipeTerminated(Pipe pipe)
{
// Drop the reference to the deallocated pipe.
assert (this.pipe == pipe || this.zapPipe == pipe || terminatingPipes.contains(pipe));
if (this.pipe == pipe) {
// If this is our current pipe, remove it
this.pipe = null;
if (hasLingerTimer) {
ioObject.cancelTimer(LINGER_TIMER_ID);
hasLingerTimer = false;
}
}
else if (zapPipe == pipe) {
zapPipe = null;
}
else {
// Remove the pipe from the detached pipes set
terminatingPipes.remove(pipe);
}
if (!isTerminating() && options.rawSocket) {
if (engine != null) {
engine.terminate();
engine = null;
}
}
// If we are waiting for pending messages to be sent, at this point
// we are sure that there will be no more messages and we can proceed
// with termination safely.
if (pending && this.pipe == null && this.zapPipe == null && terminatingPipes.isEmpty()) {
pending = false;
super.processTerm(0);
}
}
@Override
public void readActivated(Pipe pipe)
{
// Skip activating if we're detaching this pipe
if (this.pipe != pipe && this.zapPipe != pipe) {
assert (terminatingPipes.contains(pipe));
return;
}
if (engine == null) {
this.pipe.checkRead();
return;
}
if (this.pipe == pipe) {
engine.restartOutput();
}
else {
engine.zapMsgAvailable();
}
}
@Override
public void writeActivated(Pipe pipe)
{
// Skip activating if we're detaching this pipe
if (this.pipe != pipe) {
assert (terminatingPipes.contains(pipe));
return;
}
if (engine != null) {
engine.restartInput();
}
}
@Override
public void hiccuped(Pipe pipe)
{
// Hiccups are always sent from session to socket, not the other
// way round.
throw new UnsupportedOperationException("Must Override");
}
public SocketBase getSocket()
{
return socket;
}
@Override
protected void processPlug()
{
ioObject.plug();
if (active) {
startConnecting(false);
}
}
public int zapConnect()
{
// Session might be reused with zap connexion already established, don't panic
if (zapPipe == null) {
Ctx.Endpoint peer = findEndpoint("inproc://zeromq.zap.01");
if (peer.socket == null) {
errno.set(ZError.ECONNREFUSED);
return ZError.ECONNREFUSED;
}
if (peer.options.type != ZMQ.ZMQ_REP && peer.options.type != ZMQ.ZMQ_ROUTER &&
peer.options.type != ZMQ.ZMQ_SERVER) {
errno.set(ZError.ECONNREFUSED);
return ZError.ECONNREFUSED;
}
// Create a bi-directional pipe that will connect
// session with zap socket.
ZObject[] parents = { this, peer.socket };
int[] hwms = { 0, 0 };
boolean[] conflates = { false, false };
Pipe[] pipes = Pipe.pair(parents, hwms, conflates);
// Attach local end of the pipe to this socket object.
zapPipe = pipes[0];
zapPipe.setNoDelay();
zapPipe.setEventSink(this);
sendBind(peer.socket, pipes[1], false);
// Send empty identity if required by the peer.
if (peer.options.recvIdentity) {
Msg id = new Msg();
id.setFlags(Msg.IDENTITY);
zapPipe.write(id);
zapPipe.flush();
}
}
return 0;
}
protected boolean zapEnabled()
{
return options.mechanism != Mechanisms.NULL || (options.zapDomain != null && !options.zapDomain.isEmpty());
}
@Override
protected void processAttach(IEngine engine)
{
assert (engine != null);
// Create the pipe if it does not exist yet.
if (pipe == null && !isTerminating()) {
ZObject[] parents = { this, socket };
boolean conflate = options.conflate && (options.type == ZMQ.ZMQ_DEALER || options.type == ZMQ.ZMQ_PULL
|| options.type == ZMQ.ZMQ_PUSH || options.type == ZMQ.ZMQ_PUB || options.type == ZMQ.ZMQ_SUB);
int[] hwms = { conflate ? -1 : options.recvHwm, conflate ? -1 : options.sendHwm };
boolean[] conflates = { conflate, conflate };
Pipe[] pipes = Pipe.pair(parents, hwms, conflates);
// Plug the local end of the pipe.
pipes[0].setEventSink(this);
// Remember the local end of the pipe.
assert (pipe == null);
pipe = pipes[0];
// Ask socket to plug into the remote end of the pipe.
sendBind(socket, pipes[1]);
}
// Plug in the engine.
assert (this.engine == null);
this.engine = engine;
this.engine.plug(ioThread, this);
}
public void engineError(boolean handshaked, ErrorReason reason)
{
// Engine is dead. Let's forget about it.
engine = null;
// Remove any half-done messages from the pipes.
if (pipe != null) {
cleanPipes();
// Only send disconnect message if socket was accepted and handshake was completed
if (!active && handshaked && options.canReceiveDisconnectMsg && options.disconnectMsg != null) {
pipe.setDisconnectMsg(options.disconnectMsg);
pipe.sendDisconnectMsg();
}
if (active && handshaked && options.canReceiveHiccupMsg && options.hiccupMsg != null) {
pipe.sendHiccupMsg(options.hiccupMsg);
}
}
assert (reason == ErrorReason.CONNECTION || reason == ErrorReason.TIMEOUT || reason == ErrorReason.PROTOCOL);
switch (reason) {
case TIMEOUT:
case CONNECTION:
if (active) {
reconnect();
}
else {
terminate();
}
break;
case PROTOCOL:
terminate();
break;
default:
break;
}
// Just in case there's only a delimiter in the pipe.
if (pipe != null) {
pipe.checkRead();
}
if (zapPipe != null) {
zapPipe.checkRead();
}
}
@Override
protected void processTerm(int linger)
{
assert (!pending);
// If the termination of the pipe happens before the term command is
// delivered there's nothing much to do. We can proceed with the
// standard termination immediately.
if (pipe == null && zapPipe == null && terminatingPipes.isEmpty()) {
super.processTerm(0);
return;
}
pending = true;
if (pipe != null) {
// If there's finite linger value, delay the termination.
// If linger is infinite (negative) we don't even have to set
// the timer.
if (linger > 0) {
assert (!hasLingerTimer);
ioObject.addTimer(linger, LINGER_TIMER_ID);
hasLingerTimer = true;
}
// Start pipe termination process. Delay the termination till all messages
// are processed in case the linger time is non-zero.
pipe.terminate(linger != 0);
// TODO: Should this go into pipe_t::terminate ?
// In case there's no engine and there's only delimiter in the
// pipe it wouldn't be ever read. Thus we check for it explicitly.
if (engine == null) {
pipe.checkRead();
}
}
if (zapPipe != null) {
zapPipe.terminate(false);
}
}
@Override
public void timerEvent(int id)
{
// Linger period expired. We can proceed with termination even though
// there are still pending messages to be sent.
assert (id == LINGER_TIMER_ID);
hasLingerTimer = false;
// Ask pipe to terminate even though there may be pending messages in it.
assert (pipe != null);
pipe.terminate(false);
}
private void reconnect()
{
// TODO V4 - // Transient session self-destructs after peer disconnects. ?
// For delayed connect situations, terminate the pipe
// and reestablish later on
if (pipe != null && !options.immediate && !addr.protocol().isMulticast) {
pipe.hiccup();
pipe.terminate(false);
terminatingPipes.add(pipe);
pipe = null;
}
reset();
// Reconnect.
if (options.reconnectIvl != -1) {
startConnecting(true);
}
// For subscriber sockets we hiccup the inbound pipe, which will cause
// the socket object to resend all the subscriptions.
if (pipe != null && (options.type == ZMQ.ZMQ_SUB || options.type == ZMQ.ZMQ_XSUB)) {
pipe.hiccup();
}
}
private void startConnecting(boolean wait)
{
assert (active);
// Choose I/O thread to run connecter in. Given that we are already
// running in an I/O thread, there must be at least one available.
IOThread ioThread = chooseIoThread(options.affinity);
assert (ioThread != null);
// Create the connecter object.
NetProtocol protocol = addr.protocol();
if (protocol == null) {
errno.set(ZError.EPROTONOSUPPORT);
return;
}
switch (protocol) {
case tcp:
if (options.socksProxyAddress != null) {
Address proxyAddress = new Address(NetProtocol.tcp, options.socksProxyAddress);
SocksConnecter connecter = new SocksConnecter(ioThread, this, options, addr, proxyAddress, wait);
launchChild(connecter);
}
else {
TcpConnecter connecter = new TcpConnecter(ioThread, this, options, addr, wait);
launchChild(connecter);
}
break;
case ipc: {
IpcConnecter connecter = new IpcConnecter(ioThread, this, options, addr, wait);
launchChild(connecter);
}
break;
case tipc: {
TipcConnecter connecter = new TipcConnecter(ioThread, this, options, addr, wait);
launchChild(connecter);
}
break;
case pgm:
case epgm: {
assert (options.type == ZMQ.ZMQ_PUB || options.type == ZMQ.ZMQ_XPUB || options.type == ZMQ.ZMQ_SUB
|| options.type == ZMQ.ZMQ_XSUB);
// For EPGM transport with UDP encapsulation of PGM is used.
boolean udpEncapsulation = protocol == NetProtocol.epgm;
// At this point we'll create message pipes to the session straight
// away. There's no point in delaying it as no concept of 'connect'
// exists with PGM anyway.
if (options.type == ZMQ.ZMQ_PUB || options.type == ZMQ.ZMQ_XPUB) {
// PGM sender.
PgmSender pgmSender = new PgmSender(ioThread, options);
boolean rc = pgmSender.init(udpEncapsulation, addr);
assert (rc);
sendAttach(this, pgmSender);
}
else {
// PGM receiver.
PgmReceiver pgmReceiver = new PgmReceiver(ioThread, options);
boolean rc = pgmReceiver.init(udpEncapsulation, addr);
assert (rc);
sendAttach(this, pgmReceiver);
}
}
break;
case norm: {
// At this point we'll create message pipes to the session straight
// away. There's no point in delaying it as no concept of 'connect'
// exists with NORM anyway.
if (options.type == ZMQ.ZMQ_PUB || options.type == ZMQ.ZMQ_XPUB) {
// NORM sender.
NormEngine normSender = new NormEngine(ioThread, options);
boolean rc = normSender.init(addr, true, false);
assert (rc);
sendAttach(this, normSender);
}
else {
// NORM receiver.
NormEngine normReceiver = new NormEngine(ioThread, options);
boolean rc = normReceiver.init(addr, false, true);
assert (rc);
sendAttach(this, normReceiver);
}
}
break;
default:
assert (false);
break;
}
}
public String getEndpoint()
{
return engine.getEndPoint();
}
@Override
public String toString()
{
return getClass().getSimpleName() + "-" + socket;
}
@Override
public final void incSeqnum()
{
super.incSeqnum();
}
}
jeromq-0.6.0/src/main/java/zmq/io/StreamEngine.java 0000664 0000000 0000000 00000120375 14557711263 0022133 0 ustar 00root root 0000000 0000000 package zmq.io;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Arrays;
import zmq.Config;
import zmq.Msg;
import zmq.Options;
import zmq.SocketBase;
import zmq.ZError;
import zmq.ZMQ;
import zmq.io.coder.IDecoder;
import zmq.io.coder.IDecoder.Step;
import zmq.io.coder.IEncoder;
import zmq.io.coder.raw.RawDecoder;
import zmq.io.coder.raw.RawEncoder;
import zmq.io.coder.v1.V1Decoder;
import zmq.io.coder.v1.V1Encoder;
import zmq.io.coder.v2.V2Decoder;
import zmq.io.coder.v2.V2Encoder;
import zmq.io.mechanism.Mechanism;
import zmq.io.mechanism.Mechanisms;
import zmq.io.net.Address;
import zmq.poll.IPollEvents;
import zmq.poll.Poller;
import zmq.util.Blob;
import zmq.util.Errno;
import zmq.util.Utils;
import zmq.util.ValueReference;
import zmq.util.Wire;
import zmq.util.function.Function;
import zmq.util.function.Supplier;
// This engine handles any socket with SOCK_STREAM semantics,
// e.g. TCP socket or an UNIX domain socket.
public class StreamEngine implements IEngine, IPollEvents
{
private final class ProducePongMessage implements Supplier
{
private final byte[] pingContext;
public ProducePongMessage(byte[] pingContext)
{
assert (pingContext != null);
this.pingContext = pingContext;
}
@Override
public Msg get()
{
return producePongMessage(pingContext);
}
}
// Protocol revisions
private enum Protocol
{
V0(-1),
V1(0),
V2(1),
V3(3);
private final byte revision;
Protocol(int revision)
{
this.revision = (byte) revision;
}
}
public enum ErrorReason
{
PROTOCOL,
CONNECTION,
TIMEOUT,
}
private IOObject ioObject;
// Underlying socket.
private SocketChannel fd;
private Poller.Handle handle;
private ByteBuffer inpos;
private int insize;
private IDecoder decoder;
private final ValueReference outpos;
private int outsize;
private IEncoder encoder;
private Metadata metadata;
// When true, we are still trying to determine whether
// the peer is using versioned protocol, and if so, which
// version. When false, normal message flow has started.
private boolean handshaking;
private static final int SIGNATURE_SIZE = 10;
// Size of ZMTP/1.0 and ZMTP/2.0 greeting message
private static final int V2_GREETING_SIZE = 12;
// Size of ZMTP/3.0 greeting message
private static final int V3_GREETING_SIZE = 64;
// Expected greeting size.
private int greetingSize;
// Greeting received from, and sent to peer
private final ByteBuffer greetingRecv;
private final ByteBuffer greetingSend;
// handy reminder of the used ZMTP protocol version
private Protocol zmtpVersion;
// The session this engine is attached to.
private SessionBase session;
private final Options options;
// String representation of endpoint
private final String endpoint;
private boolean plugged;
private Supplier nextMsg;
private Function processMsg;
private boolean ioError;
// Indicates whether the engine is to inject a phantom
// subscription message into the incoming stream.
// Needed to support old peers.
private boolean subscriptionRequired;
private Mechanism mechanism;
// True if the engine couldn't consume the last decoded message.
private boolean inputStopped;
// True if the engine doesn't have any message to encode.
private boolean outputStopped;
// ID of the handshake timer
private static final int HANDSHAKE_TIMER_ID = 0x40;
private static final int HEARTBEAT_TTL_TIMER_ID = 0x80;
private static final int HEARTBEAT_IVL_TIMER_ID = 0x81;
private static final int HEARTBEAT_TIMEOUT_TIMER_ID = 0x82;
// True is linger timer is running.
private boolean hasHandshakeTimer;
private boolean hasTtlTimer;
private boolean hasTimeoutTimer;
private boolean hasHeartbeatTimer;
private final int heartbeatTimeout;
private final byte[] heartbeatContext;
// Socket
private SocketBase socket;
private final Address peerAddress;
private final Address selfAddress;
private final Errno errno;
public StreamEngine(SocketChannel fd, final Options options, final String endpoint)
{
this.errno = options.errno;
this.fd = fd;
this.handshaking = true;
greetingSize = V2_GREETING_SIZE;
this.options = options;
this.endpoint = endpoint;
Supplier nextIdentity = this::identityMsg;
nextMsg = nextIdentity;
processMsg = processIdentity;
outpos = new ValueReference<>();
greetingRecv = ByteBuffer.allocate(V3_GREETING_SIZE);
greetingSend = ByteBuffer.allocate(V3_GREETING_SIZE);
// Put the socket into non-blocking mode.
try {
Utils.unblockSocket(this.fd);
}
catch (IOException e) {
throw new ZError.IOException(e);
}
peerAddress = Utils.getPeerIpAddress(fd);
selfAddress = Utils.getLocalIpAddress(fd);
heartbeatTimeout = heartbeatTimeout();
heartbeatContext = Arrays.copyOf(options.heartbeatContext, options.heartbeatContext.length);
}
private int heartbeatTimeout()
{
int timeout = 0;
if (options.heartbeatInterval > 0) {
timeout = options.heartbeatTimeout;
if (timeout == -1) {
timeout = options.heartbeatInterval;
}
}
return timeout;
}
public void destroy()
{
assert (!plugged);
if (fd != null) {
try {
fd.close();
}
catch (IOException e) {
assert (false);
}
fd = null;
}
if (encoder != null) {
encoder.destroy();
}
if (decoder != null) {
decoder.destroy();
}
if (mechanism != null) {
mechanism.destroy();
}
}
@Override
public void plug(IOThread ioThread, SessionBase session)
{
assert (!plugged);
plugged = true;
// Connect to session object.
assert (this.session == null);
assert (session != null);
this.session = session;
socket = session.getSocket();
// Connect to I/O threads poller object.
ioObject = new IOObject(ioThread, this);
ioObject.plug();
handle = ioObject.addFd(fd);
ioError = false;
// Make sure batch sizes match large buffer sizes
final int inBatchSize = Math.max(options.rcvbuf, Config.IN_BATCH_SIZE.getValue());
final int outBatchSize = Math.max(options.sndbuf, Config.OUT_BATCH_SIZE.getValue());
if (options.rawSocket) {
decoder = instantiate(options.decoder, inBatchSize, options.maxMsgSize);
if (decoder == null) {
decoder = new RawDecoder(inBatchSize);
}
encoder = instantiate(options.encoder, outBatchSize, options.maxMsgSize);
if (encoder == null) {
encoder = new RawEncoder(errno, outBatchSize);
}
// disable handshaking for raw socket
handshaking = false;
nextMsg = pullMsgFromSession;
processMsg = pushRawMsgToSession;
if (peerAddress != null && !peerAddress.address().isEmpty()) {
assert (metadata == null);
// Compile metadata
metadata = new Metadata();
metadata.put(Metadata.PEER_ADDRESS, peerAddress.address());
}
if (options.selfAddressPropertyName != null && ! options.selfAddressPropertyName.isEmpty()
&& selfAddress != null && !selfAddress.address().isEmpty()) {
if (metadata == null) {
metadata = new Metadata();
}
metadata.put(options.selfAddressPropertyName, selfAddress.address());
}
// For raw sockets, send an initial 0-length message to the
// application so that it knows a peer has connected.
Msg connector = new Msg();
pushRawMsgToSession(connector);
session.flush();
}
else {
// start optional timer, to prevent handshake hanging on no input
setHandshakeTimer();
// Send the 'length' and 'flags' fields of the identity message.
// The 'length' field is encoded in the long format.
greetingSend.put((byte) 0xff);
Wire.putUInt64(greetingSend, options.identitySize + 1);
greetingSend.put((byte) 0x7f);
outpos.set(greetingSend);
outsize = greetingSend.position();
greetingSend.flip();
}
ioObject.setPollIn(handle);
ioObject.setPollOut(handle);
// Flush all the data that may have been already received downstream.
inEvent();
}
private T instantiate(Class clazz, int size, long max)
{
if (clazz == null) {
return null;
}
try {
return clazz.getConstructor(int.class, long.class).newInstance(size, max);
}
catch (InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
throw new ZError.InstantiationException(e);
}
}
private void unplug()
{
assert (plugged);
plugged = false;
// Cancel all timers.
if (hasHandshakeTimer) {
ioObject.cancelTimer(HANDSHAKE_TIMER_ID);
hasHandshakeTimer = false;
}
if (hasTtlTimer) {
ioObject.cancelTimer(HEARTBEAT_TTL_TIMER_ID);
hasTtlTimer = false;
}
if (hasTimeoutTimer) {
ioObject.cancelTimer(HEARTBEAT_TIMEOUT_TIMER_ID);
hasTimeoutTimer = false;
}
if (hasHeartbeatTimer) {
ioObject.cancelTimer(HEARTBEAT_IVL_TIMER_ID);
hasHeartbeatTimer = false;
}
if (!ioError) {
// Cancel all fd subscriptions.
ioObject.removeHandle(handle);
handle = null;
}
// Disconnect from I/O threads poller object.
ioObject.unplug();
session = null;
}
@Override
public void terminate()
{
unplug();
destroy();
}
@Override
public void inEvent()
{
assert (!ioError);
// If still handshaking, receive and process the greeting message.
if (handshaking) {
if (!handshake()) {
return;
}
}
assert (decoder != null);
// If there has been an I/O error, stop polling.
if (inputStopped) {
ioObject.removeHandle(handle);
handle = null;
ioError = true;
return;
}
// If there's no data to process in the buffer...
if (insize == 0) {
// Retrieve the buffer and read as much data as possible.
// Note that buffer can be arbitrarily large. However, we assume
// the underlying TCP layer has fixed buffer size and thus the
// number of bytes read will be always limited.
inpos = decoder.getBuffer();
int rc = read(inpos);
if (rc == 0) {
error(ErrorReason.CONNECTION);
}
if (rc == -1) {
if (!errno.is(ZError.EAGAIN)) {
error(ErrorReason.CONNECTION);
}
return;
}
// Adjust input size
inpos.flip();
insize = rc;
}
boolean rc = false;
ValueReference processed = new ValueReference<>(0);
while (insize > 0) {
// Push the data to the decoder.
Step.Result result = decoder.decode(inpos, insize, processed);
assert (processed.get() <= insize);
insize -= processed.get();
if (result == Step.Result.MORE_DATA) {
rc = true;
break;
}
if (result == Step.Result.ERROR) {
rc = false;
break;
}
Msg msg = decoder.msg();
rc = processMsg.apply(msg);
if (!rc) {
break;
}
}
// Tear down the connection if we have failed to decode input data
// or the session has rejected the message.
if (!rc) {
if (!errno.is(ZError.EAGAIN)) {
error(ErrorReason.PROTOCOL);
return;
}
inputStopped = true;
ioObject.resetPollIn(handle);
}
// Flush all messages the decoder may have produced.
session.flush();
}
@Override
public void outEvent()
{
assert (!ioError);
// If write buffer is empty, try to read new data from the encoder.
if (outsize == 0) {
// Even when we stop polling as soon as there is no
// data to send, the poller may invoke outEvent one
// more time due to 'speculative write' optimization.
if (encoder == null) {
assert (handshaking);
return;
}
outpos.set(null);
outsize = encoder.encode(outpos, 0);
// Make sure batch sizes match large buffer sizes
final int outBatchSize = Math.max(options.sndbuf, Config.OUT_BATCH_SIZE.getValue());
while (outsize < outBatchSize) {
Msg msg = nextMsg.get();
if (msg == null) {
break;
}
encoder.loadMsg(msg);
int n = encoder.encode(outpos, outBatchSize - outsize);
assert (n > 0);
outsize += n;
}
// If there is no data to send, stop polling for output.
if (outsize == 0) {
outputStopped = true;
ioObject.resetPollOut(handle);
return;
}
// slight difference with libzmq:
// encoder is notified of the end of the loading
encoder.encoded();
}
// If there are any data to write in write buffer, write as much as
// possible to the socket. Note that amount of data to write can be
// arbitrarily large. However, we assume that underlying TCP layer has
// limited transmission buffer and thus the actual number of bytes
// written should be reasonably modest.
int nbytes = write(outpos.get());
// IO error has occurred. We stop waiting for output events.
// The engine is not terminated until we detect input error;
// this is necessary to prevent losing incoming messages.
if (nbytes == -1) {
ioObject.resetPollOut(handle);
return;
}
outsize -= nbytes;
// If we are still handshaking and there are no data
// to send, stop polling for output.
if (handshaking) {
if (outsize == 0) {
ioObject.resetPollOut(handle);
}
}
}
@Override
public void restartOutput()
{
if (ioError) {
return;
}
if (outputStopped) {
ioObject.setPollOut(handle);
outputStopped = false;
}
// Speculative write: The assumption is that at the moment new message
// was sent by the user the socket is probably available for writing.
// Thus we try to write the data to socket avoiding polling for POLLOUT.
// Consequently, the latency should be better in request/reply scenarios.
outEvent();
}
@Override
public void restartInput()
{
assert (inputStopped);
assert (session != null);
assert (decoder != null);
Msg msg = decoder.msg();
if (!processMsg.apply(msg)) {
if (errno.is(ZError.EAGAIN)) {
session.flush();
}
else {
error(ErrorReason.PROTOCOL);
}
return;
}
boolean decodingSuccess = decodeCurrentInputs();
if (!decodingSuccess && errno.is(ZError.EAGAIN)) {
session.flush();
}
else if (ioError) {
error(ErrorReason.CONNECTION);
}
else if (!decodingSuccess) {
error(ErrorReason.PROTOCOL);
}
else {
inputStopped = false;
ioObject.setPollIn(handle);
session.flush();
// Speculative read.
inEvent();
}
}
private boolean decodeCurrentInputs()
{
while (insize > 0) {
ValueReference processed = new ValueReference<>(0);
Step.Result result = decoder.decode(inpos, insize, processed);
assert (processed.get() <= insize);
insize -= processed.get();
if (result == Step.Result.MORE_DATA) {
return true;
}
if (result == Step.Result.ERROR) {
return false;
}
if (!processMsg.apply(decoder.msg())) {
return false;
}
}
return true;
}
// Detects the protocol used by the peer.
private boolean handshake()
{
assert (handshaking);
assert (greetingRecv.position() < greetingSize);
final Mechanisms mechanism = options.mechanism;
assert (mechanism != null);
// Position of the version field in the greeting.
final int revisionPos = SIGNATURE_SIZE;
// Make sure batch sizes match large buffer sizes
final int inBatchSize = Math.max(options.rcvbuf, Config.IN_BATCH_SIZE.getValue());
final int outBatchSize = Math.max(options.sndbuf, Config.OUT_BATCH_SIZE.getValue());
// Receive the greeting.
while (greetingRecv.position() < greetingSize) {
final int n = read(greetingRecv);
if (n == 0) {
error(ErrorReason.CONNECTION);
return false;
}
if (n == -1) {
if (!errno.is(ZError.EAGAIN)) {
error(ErrorReason.CONNECTION);
}
return false;
}
// We have received at least one byte from the peer.
// If the first byte is not 0xff, we know that the
// peer is using unversioned protocol.
if ((greetingRecv.get(0) & 0xff) != 0xff) {
// If this first byte is not %FF,
// then the other peer is using ZMTP 1.0.
break;
}
if (greetingRecv.position() < SIGNATURE_SIZE) {
continue;
}
// Inspect the right-most bit of the 10th byte (which coincides
// with the 'flags' field if a regular message was sent).
// Zero indicates this is a header of identity message
// (i.e. the peer is using the unversioned protocol).
if ((greetingRecv.get(9) & 0x01) != 0x01) {
break;
}
// If the least significant bit is 1, the peer is using ZMTP 2.0 or later
// and has sent us the ZMTP signature.
int outpos = greetingSend.position();
// The peer is using versioned protocol.
// Send the major version number.
if (greetingSend.limit() == SIGNATURE_SIZE) {
if (outsize == 0) {
ioObject.setPollOut(handle);
}
greetingSend.limit(SIGNATURE_SIZE + 1);
greetingSend.put(revisionPos, Protocol.V3.revision); // Major version number
outsize += 1;
}
if (greetingRecv.position() > SIGNATURE_SIZE) {
if (greetingSend.limit() == SIGNATURE_SIZE + 1) {
if (outsize == 0) {
ioObject.setPollOut(handle);
}
// We read a further byte, which indicates the ZMTP version.
byte protocol = greetingRecv.get(revisionPos);
if (protocol == Protocol.V1.revision || protocol == Protocol.V2.revision) {
// If this is V1 or V2, we have a ZMTP 2.0 peer.
greetingSend.limit(V2_GREETING_SIZE);
greetingSend.position(SIGNATURE_SIZE + 1);
greetingSend.put((byte) options.type); // Socket type
outsize += 1;
}
else {
// If this is 3 or greater, we have a ZMTP 3.0 peer.
greetingSend.limit(V3_GREETING_SIZE);
greetingSend.position(SIGNATURE_SIZE + 1);
greetingSend.put((byte) 0); // Minor version number
outsize += 1;
greetingSend.mark();
greetingSend.put(new byte[20]);
assert (mechanism == Mechanisms.NULL || mechanism == Mechanisms.PLAIN
|| mechanism == Mechanisms.CURVE || mechanism == Mechanisms.GSSAPI);
greetingSend.reset();
greetingSend.put(mechanism.name().getBytes(ZMQ.CHARSET));
greetingSend.reset();
greetingSend.position(greetingSend.position() + 20);
outsize += 20;
greetingSend.put(new byte[32]);
outsize += 32;
greetingSize = V3_GREETING_SIZE;
}
}
}
greetingSend.position(outpos);
}
// Is the peer using the unversioned protocol?
// If so, we send and receive rest of identity
// messages.
if ((greetingRecv.get(0) & 0xff) != 0xff || (greetingRecv.get(9) & 0x01) == 0) {
// If this first byte is %FF, then we read nine further bytes,
// and inspect the last byte (the 10th in total).
// If the least significant bit is 0, then the other peer is using ZMTP 1.0.
if (session.zapEnabled()) {
// reject ZMTP 1.0 connections if ZAP is enabled
error(ErrorReason.PROTOCOL);
return false;
}
zmtpVersion = Protocol.V0;
encoder = new V1Encoder(errno, outBatchSize);
decoder = new V1Decoder(errno, inBatchSize, options.maxMsgSize, options.allocator);
// We have already sent the message header.
// Since there is no way to tell the encoder to
// skip the message header, we simply throw that
// header data away.
final int headerSize = options.identitySize + 1 >= 255 ? 10 : 2;
ByteBuffer tmp = ByteBuffer.allocate(headerSize);
// Prepare the identity message and load it into encoder.
// Then consume bytes we have already sent to the peer.
Msg txMsg = new Msg(options.identitySize);
txMsg.put(options.identity, 0, options.identitySize);
encoder.loadMsg(txMsg);
ValueReference bufferp = new ValueReference<>(tmp);
int bufferSize = encoder.encode(bufferp, headerSize);
assert (bufferSize == headerSize);
// Make sure the decoder sees the data we have already received.
decodeDataAfterHandshake(0);
// To allow for interoperability with peers that do not forward
// their subscriptions, we inject a phantom subscription message
// message into the incoming message stream.
if (options.type == ZMQ.ZMQ_PUB || options.type == ZMQ.ZMQ_XPUB) {
subscriptionRequired = true;
}
// We are sending our identity now and the next message
// will come from the socket.
nextMsg = pullMsgFromSession;
// We are expecting identity message.
processMsg = processIdentity;
}
else if (greetingRecv.get(revisionPos) == Protocol.V1.revision) {
// ZMTP/1.0 framing.
zmtpVersion = Protocol.V1;
if (session.zapEnabled()) {
// reject ZMTP 1.0 connections if ZAP is enabled
error(ErrorReason.PROTOCOL);
return false;
}
encoder = new V1Encoder(errno, outBatchSize);
decoder = new V1Decoder(errno, inBatchSize, options.maxMsgSize, options.allocator);
decodeDataAfterHandshake(V2_GREETING_SIZE);
}
else if (greetingRecv.get(revisionPos) == Protocol.V2.revision) {
// ZMTP/2.0 framing.
zmtpVersion = Protocol.V2;
if (session.zapEnabled()) {
// reject ZMTP 2.0 connections if ZAP is enabled
error(ErrorReason.PROTOCOL);
return false;
}
encoder = new V2Encoder(errno, outBatchSize);
decoder = new V2Decoder(errno, inBatchSize, options.maxMsgSize, options.allocator);
decodeDataAfterHandshake(V2_GREETING_SIZE);
}
else {
zmtpVersion = Protocol.V3;
encoder = new V2Encoder(errno, outBatchSize);
decoder = new V2Decoder(errno, inBatchSize, options.maxMsgSize, options.allocator);
greetingRecv.position(V2_GREETING_SIZE);
if (mechanism.isMechanism(greetingRecv)) {
this.mechanism = mechanism.create(session, peerAddress, options);
}
else {
error(ErrorReason.PROTOCOL);
return false;
}
nextMsg = nextHandshakeCommand;
processMsg = processHandshakeCommand;
}
// Start polling for output if necessary.
if (outsize == 0) {
ioObject.setPollOut(handle);
}
// Handshaking was successful.
// Switch into the normal message flow.
handshaking = false;
if (hasHandshakeTimer) {
ioObject.cancelTimer(HANDSHAKE_TIMER_ID);
hasHandshakeTimer = false;
}
socket.eventHandshaken(endpoint, zmtpVersion.ordinal());
return true;
}
private void decodeDataAfterHandshake(int greetingSize)
{
final int pos = greetingRecv.position();
if (pos > greetingSize) {
// data is present after handshake
greetingRecv.position(greetingSize).limit(pos);
// Make sure the decoder sees this extra data.
inpos = greetingRecv;
insize = greetingRecv.remaining();
}
}
private Msg identityMsg()
{
Msg msg = new Msg(options.identitySize);
if (options.identitySize > 0) {
msg.put(options.identity, 0, options.identitySize);
}
nextMsg = pullMsgFromSession;
return msg;
}
private boolean processIdentityMsg(Msg msg)
{
if (options.recvIdentity) {
msg.setFlags(Msg.IDENTITY);
boolean rc = session.pushMsg(msg);
assert (rc);
}
if (subscriptionRequired) {
// Inject the subscription message, so that also
// ZMQ 2.x peers receive published messages.
Msg subscription = new Msg(1);
subscription.put((byte) 1);
boolean rc = session.pushMsg(subscription);
assert (rc);
}
processMsg = pushMsgToSession;
return true;
}
private final Function processIdentity = this::processIdentityMsg;
private Msg nextHandshakeCommand()
{
assert (mechanism != null);
if (mechanism.status() == Mechanism.Status.READY) {
mechanismReady();
return pullAndEncode.get();
}
else if (mechanism.status() == Mechanism.Status.ERROR) {
errno.set(ZError.EPROTO);
// error(ErrorReason.PROTOCOL);
return null;
}
else {
Msg.Builder msg = new Msg.Builder();
int rc = mechanism.nextHandshakeCommand(msg);
if (rc == 0) {
msg.setFlags(Msg.COMMAND);
return msg.build();
}
else {
errno.set(rc);
return null;
}
}
}
private boolean processHandshakeCommand(Msg msg)
{
assert (mechanism != null);
int rc = mechanism.processHandshakeCommand(msg);
if (rc == 0) {
if (mechanism.status() == Mechanism.Status.READY) {
mechanismReady();
}
else if (mechanism.status() == Mechanism.Status.ERROR) {
errno.set(ZError.EPROTO);
return false;
}
if (outputStopped) {
restartOutput();
}
}
else {
errno.set(rc);
}
return rc == 0;
}
private final Function processHandshakeCommand = this::processHandshakeCommand;
private final Supplier nextHandshakeCommand = this::nextHandshakeCommand;
@Override
public void zapMsgAvailable()
{
assert (mechanism != null);
int rc = mechanism.zapMsgAvailable();
if (rc == -1) {
error(ErrorReason.PROTOCOL);
return;
}
if (inputStopped) {
restartInput();
}
if (outputStopped) {
restartOutput();
}
}
private void mechanismReady()
{
if (options.heartbeatInterval > 0) {
ioObject.addTimer(options.heartbeatInterval, HEARTBEAT_IVL_TIMER_ID);
hasHeartbeatTimer = true;
}
if (options.recvIdentity) {
Msg identity = mechanism.peerIdentity();
boolean rc = session.pushMsg(identity);
if (!rc && errno.is(ZError.EAGAIN)) {
// If the write is failing at this stage with
// an EAGAIN the pipe must be being shut down,
// so we can just bail out of the identity set.
return;
}
assert (rc);
session.flush();
}
nextMsg = pullAndEncode;
processMsg = writeCredential;
// Compile metadata.
assert (metadata == null);
metadata = new Metadata();
// If we have a peer_address, add it to metadata
if (peerAddress != null && !peerAddress.address().isEmpty()) {
metadata.set(Metadata.PEER_ADDRESS, peerAddress.address());
}
// If we have a local_address, add it to metadata
if (options.selfAddressPropertyName != null && ! options.selfAddressPropertyName.isEmpty()
&& selfAddress != null && !selfAddress.address().isEmpty()) {
metadata.put(options.selfAddressPropertyName, selfAddress.address());
}
// Add ZAP properties.
metadata.set(mechanism.zapProperties);
// Add ZMTP properties.
metadata.set(mechanism.zmtpProperties);
if (metadata.isEmpty()) {
metadata = null;
}
}
private Msg pullMsgFromSession()
{
return session.pullMsg();
}
private boolean pushMsgToSession(Msg msg)
{
return session.pushMsg(msg);
}
private final Function pushMsgToSession = this::pushMsgToSession;
private final Supplier pullMsgFromSession = this::pullMsgFromSession;
private boolean pushRawMsgToSession(Msg msg)
{
if (metadata != null && !metadata.equals(msg.getMetadata())) {
msg.setMetadata(metadata);
}
return pushMsgToSession(msg);
}
private final Function pushRawMsgToSession = this::pushRawMsgToSession;
private boolean writeCredential(Msg msg)
{
assert (mechanism != null);
assert (session != null);
Blob credential = mechanism.getUserId();
if (credential != null && credential.size() > 0) {
Msg cred = new Msg(credential.size());
cred.put(credential.data(), 0, credential.size());
cred.setFlags(Msg.CREDENTIAL);
boolean rc = session.pushMsg(cred);
if (!rc) {
return false;
}
}
processMsg = decodeAndPush;
return decodeAndPush.apply(msg);
}
private final Function writeCredential = this::writeCredential;
private Msg pullAndEncode()
{
assert (mechanism != null);
Msg msg = session.pullMsg();
if (msg == null) {
return null;
}
msg = mechanism.encode(msg);
return msg;
}
private final Supplier pullAndEncode = this::pullAndEncode;
private boolean decodeAndPush(Msg msg)
{
assert (mechanism != null);
msg = mechanism.decode(msg);
if (msg == null) {
return false;
}
if (hasTimeoutTimer) {
hasTimeoutTimer = false;
ioObject.cancelTimer(HEARTBEAT_TIMEOUT_TIMER_ID);
}
if (hasTtlTimer) {
hasTtlTimer = false;
ioObject.cancelTimer(HEARTBEAT_TTL_TIMER_ID);
}
if (msg.isCommand()) {
StreamEngine.this.processCommand(msg);
}
if (metadata != null) {
msg.setMetadata(metadata);
}
boolean rc = session.pushMsg(msg);
if (!rc) {
if (errno.is(ZError.EAGAIN)) {
processMsg = pushOneThenDecodeAndPush;
}
return false;
}
return true;
}
private final Function decodeAndPush = this::decodeAndPush;
private boolean pushOneThenDecodeAndPush(Msg msg)
{
boolean rc = session.pushMsg(msg);
if (rc) {
processMsg = decodeAndPush;
}
return rc;
}
private final Function pushOneThenDecodeAndPush = this::pushOneThenDecodeAndPush;
private final Supplier producePingMessage = this::producePingMessage;
// Function to handle network disconnections.
private void error(ErrorReason error)
{
if (options.rawSocket) {
// For raw sockets, send a final 0-length message to the application
// so that it knows the peer has been disconnected.
Msg terminator = new Msg();
processMsg.apply(terminator);
}
assert (session != null);
socket.eventDisconnected(endpoint, fd);
session.flush();
session.engineError(!handshaking && (mechanism == null ||
mechanism.status() != Mechanism.Status.HANDSHAKING), error);
unplug();
destroy();
}
private void setHandshakeTimer()
{
assert (!hasHandshakeTimer);
if (!options.rawSocket && options.handshakeIvl > 0) {
ioObject.addTimer(options.handshakeIvl, HANDSHAKE_TIMER_ID);
hasHandshakeTimer = true;
}
}
@Override
public void timerEvent(int id)
{
if (id == HANDSHAKE_TIMER_ID) {
hasHandshakeTimer = false;
// handshake timer expired before handshake completed, so engine fails
error(ErrorReason.TIMEOUT);
}
else if (id == HEARTBEAT_IVL_TIMER_ID) {
nextMsg = producePingMessage;
outEvent();
ioObject.addTimer(options.heartbeatInterval, HEARTBEAT_IVL_TIMER_ID);
}
else if (id == HEARTBEAT_TTL_TIMER_ID) {
hasTtlTimer = false;
error(ErrorReason.TIMEOUT);
}
else if (id == HEARTBEAT_TIMEOUT_TIMER_ID) {
hasTimeoutTimer = false;
error(ErrorReason.TIMEOUT);
}
else {
// There are no other valid timer ids!
assert (false);
}
}
private Msg producePingMessage()
{
assert (mechanism != null);
Msg msg = new Msg(7 + heartbeatContext.length);
msg.setFlags(Msg.COMMAND);
msg.putShortString("PING");
Wire.putUInt16(msg, options.heartbeatTtl);
msg.put(heartbeatContext);
msg = mechanism.encode(msg);
nextMsg = pullAndEncode;
if (!hasTimeoutTimer && heartbeatTimeout > 0) {
ioObject.addTimer(heartbeatTimeout, HEARTBEAT_TIMEOUT_TIMER_ID);
hasTimeoutTimer = true;
}
return msg;
}
private Msg producePongMessage(byte[] pingContext)
{
assert (mechanism != null);
assert (pingContext != null);
Msg msg = new Msg(5 + pingContext.length);
msg.setFlags(Msg.COMMAND);
msg.putShortString("PONG");
msg.put(pingContext);
msg = mechanism.encode(msg);
nextMsg = pullAndEncode;
return msg;
}
private boolean processCommand(Msg msg)
{
if (Msgs.startsWith(msg, "PING", true)) {
return processHeartbeatMessage(msg);
}
return false;
}
private boolean processHeartbeatMessage(Msg msg)
{
// Get the remote heartbeat TTL to setup the timer
int remoteHeartbeatTtl = msg.getShort(5);
// The remote heartbeat is in 10ths of a second
// so we multiply it by 100 to get the timer interval in ms.
remoteHeartbeatTtl *= 100;
if (!hasTtlTimer && remoteHeartbeatTtl > 0) {
ioObject.addTimer(remoteHeartbeatTtl, HEARTBEAT_TTL_TIMER_ID);
hasTtlTimer = true;
}
// extract the ping context that will be sent back inside the pong message
int remaining = msg.size() - 7;
// As per ZMTP 3.1 the PING command might contain an up to 16 bytes
// context which needs to be PONGed back, so build the pong message
// here and store it. Truncate it if it's too long.
// Given the engine goes straight to outEvent(), sequential PINGs will
// not be a problem.
if (remaining > 16) {
remaining = 16;
}
final byte[] pingContext = new byte[remaining];
msg.getBytes(7, pingContext, 0, remaining);
nextMsg = new ProducePongMessage(pingContext);
outEvent();
return true;
}
// Writes data to the socket. Returns the number of bytes actually
// written (even zero is to be considered to be a success). In case
// of error or orderly shutdown by the other peer -1 is returned.
private int write(ByteBuffer outbuf)
{
int nbytes;
try {
nbytes = fd.write(outbuf);
if (nbytes == 0) {
errno.set(ZError.EAGAIN);
}
}
catch (IOException e) {
errno.set(ZError.ENOTCONN);
nbytes = -1;
}
return nbytes;
}
// Reads data from the socket (up to 'size' bytes).
// Returns the number of bytes actually read or -1 on error.
// Zero indicates the peer has closed the connection.
private int read(ByteBuffer buf)
{
int nbytes;
try {
nbytes = fd.read(buf);
if (nbytes == -1) {
errno.set(ZError.ENOTCONN);
}
else if (nbytes == 0) {
if (!fd.isBlocking()) {
// If not a single byte can be read from the socket in non-blocking mode
// we'll get an error (this may happen during the speculative read).
// Several errors are OK. When speculative read is being done we may not
// be able to read a single byte from the socket. Also, SIGSTOP issued
// by a debugging tool can result in EINTR error.
errno.set(ZError.EAGAIN);
nbytes = -1;
}
}
}
catch (IOException e) {
errno.set(ZError.ENOTCONN);
nbytes = -1;
}
return nbytes;
}
@Override
public String getEndPoint()
{
return endpoint;
}
@Override
public String toString()
{
return getClass().getSimpleName() + socket + "-" + zmtpVersion;
}
}
jeromq-0.6.0/src/main/java/zmq/io/coder/ 0000775 0000000 0000000 00000000000 14557711263 0017773 5 ustar 00root root 0000000 0000000 jeromq-0.6.0/src/main/java/zmq/io/coder/Decoder.java 0000664 0000000 0000000 00000007312 14557711263 0022206 0 ustar 00root root 0000000 0000000 package zmq.io.coder;
import zmq.Msg;
import zmq.ZError;
import zmq.msg.MsgAllocator;
import zmq.util.Errno;
// Helper base class for decoders that know the amount of data to read
// in advance at any moment. Knowing the amount in advance is a property
// of the protocol used. 0MQ framing protocol is based size-prefixed
// paradigm, which qualifies it to be parsed by this class.
// On the other hand, XML-based transports (like XMPP or SOAP) don't allow
// for knowing the size of data to read in advance and should use different
// decoding algorithms.
//
// This class implements the state machine that parses the incoming buffer.
// Derived class should implement individual state machine actions.
public abstract class Decoder extends DecoderBase
{
private final class MessageReady implements Step
{
@Override
public Step.Result apply()
{
return messageReady();
}
}
private final class FlagsReady implements Step
{
@Override
public Step.Result apply()
{
return flagsReady();
}
}
private final class EightByteSizeReady implements Step
{
@Override
public Step.Result apply()
{
return eightByteSizeReady();
}
}
private final class OneByteSizeReady implements Step
{
@Override
public Step.Result apply()
{
return oneByteSizeReady();
}
}
protected final long maxmsgsize;
// the message decoded so far
protected Msg inProgress;
protected final Step oneByteSizeReady = new OneByteSizeReady();
protected final Step eightByteSizeReady = new EightByteSizeReady();
protected final Step flagsReady = new FlagsReady();
protected final Step messageReady = new MessageReady();
private final MsgAllocator allocator;
public Decoder(Errno errno, int bufsize, long maxmsgsize, MsgAllocator allocator)
{
super(errno, bufsize);
this.maxmsgsize = maxmsgsize;
this.allocator = allocator;
}
protected final Step.Result sizeReady(final long size)
{
// Message size must not exceed the maximum allowed size.
if (maxmsgsize >= 0) {
if (size > maxmsgsize) {
errno(ZError.EMSGSIZE);
return Step.Result.ERROR;
}
}
// Message size must fit within range of size_t data type.
if (size > Integer.MAX_VALUE) {
errno(ZError.EMSGSIZE);
return Step.Result.ERROR;
}
// inProgress is initialized at this point so in theory we should
// close it before calling init_size, however, it's a 0-byte
// message and thus we can treat it as uninitialized.
inProgress = allocate((int) size);
return Step.Result.MORE_DATA;
}
protected Msg allocate(final int size)
{
return allocator.allocate(size);
}
protected Step.Result oneByteSizeReady()
{
throw new UnsupportedOperationException("Have you forgot to implement oneByteSizeReady ?");
}
protected Step.Result eightByteSizeReady()
{
throw new UnsupportedOperationException("Have you forgot to implement eightByteSizeReady ?");
}
protected Step.Result flagsReady()
{
throw new UnsupportedOperationException("Have you forgot to implement flagsReady ?");
}
protected Step.Result messageReady()
{
throw new UnsupportedOperationException("Have you forgot to implement messageReady ?");
}
protected Step.Result messageIncomplete()
{
return Step.Result.MORE_DATA;
}
@Override
public Msg msg()
{
return inProgress;
}
}
jeromq-0.6.0/src/main/java/zmq/io/coder/DecoderBase.java 0000664 0000000 0000000 00000011224 14557711263 0022776 0 ustar 00root root 0000000 0000000 package zmq.io.coder;
import java.nio.ByteBuffer;
import zmq.Msg;
import zmq.util.Errno;
import zmq.util.ValueReference;
// Helper base class for decoders that know the amount of data to read
// in advance at any moment. Knowing the amount in advance is a property
// of the protocol used. 0MQ framing protocol is based size-prefixed
// paradigm, which qualifies it to be parsed by this class.
// On the other hand, XML-based transports (like XMPP or SOAP) don't allow
// for knowing the size of data to read in advance and should use different
// decoding algorithms.
//
// This class implements the state machine that parses the incoming buffer.
// Derived class should implement individual state machine actions.
public abstract class DecoderBase implements IDecoder
{
// Where to store the read data.
private ByteBuffer readPos;
// TODO V4 remove zeroCopy boolean
private boolean zeroCopy;
// How much data to read before taking next step.
private int toRead;
// The buffer for data to decode.
private final int bufsize;
private final ByteBuffer buf;
private Step next;
private final Errno errno;
public DecoderBase(Errno errno, int bufsize)
{
next = null;
readPos = null;
toRead = 0;
this.bufsize = bufsize;
assert (bufsize > 0);
buf = ByteBuffer.allocateDirect(bufsize);
this.errno = errno;
}
// Returns a buffer to be filled with binary data.
@Override
public ByteBuffer getBuffer()
{
// If we are expected to read large message, we'll opt for zero-
// copy, i.e. we'll ask caller to fill the data directly to the
// message. Note that subsequent read(s) are non-blocking, thus
// each single read reads at most SO_RCVBUF bytes at once not
// depending on how large is the chunk returned from here.
// As a consequence, large messages being received won't block
// other engines running in the same I/O thread for excessive
// amounts of time.
if (toRead >= bufsize) {
zeroCopy = true;
return readPos.duplicate();
}
else {
zeroCopy = false;
buf.clear();
return buf;
}
}
// Processes the data in the buffer previously allocated using
// get_buffer function. size_ argument specifies number of bytes
// actually filled into the buffer. Function returns number of
// bytes actually processed.
@Override
public Step.Result decode(ByteBuffer data, int size, ValueReference processed)
{
processed.set(0);
// In case of zero-copy simply adjust the pointers, no copying
// is required. Also, run the state machine in case all the data
// were processed.
if (zeroCopy) {
assert (size <= toRead);
readPos.position(readPos.position() + size);
toRead -= size;
processed.set(size);
while (readPos.remaining() == 0) {
Step.Result result = next.apply();
if (result != Step.Result.MORE_DATA) {
return result;
}
}
return Step.Result.MORE_DATA;
}
while (processed.get() < size) {
// Copy the data from buffer to the message.
int toCopy = Math.min(toRead, size - processed.get());
int limit = data.limit();
data.limit(data.position() + toCopy);
readPos.put(data);
data.limit(limit);
toRead -= toCopy;
processed.set(processed.get() + toCopy);
// Try to get more space in the message to fill in.
// If none is available, return.
while (readPos.remaining() == 0) {
Step.Result result = next.apply();
if (result != Step.Result.MORE_DATA) {
return result;
}
}
}
return Step.Result.MORE_DATA;
}
protected void nextStep(Msg msg, Step next)
{
nextStep(msg.buf(), next);
}
@Deprecated
protected void nextStep(byte[] buf, int toRead, Step next)
{
readPos = ByteBuffer.wrap(buf);
readPos.limit(toRead);
this.toRead = toRead;
this.next = next;
}
protected void nextStep(ByteBuffer buf, Step next)
{
readPos = buf;
this.toRead = buf.remaining();
this.next = next;
}
protected void errno(int err)
{
this.errno.set(err);
}
public int errno()
{
return errno.get();
}
@Override
public void destroy()
{
}
}
jeromq-0.6.0/src/main/java/zmq/io/coder/Encoder.java 0000664 0000000 0000000 00000000623 14557711263 0022216 0 ustar 00root root 0000000 0000000 package zmq.io.coder;
import zmq.util.Errno;
public abstract class Encoder extends EncoderBase
{
protected final Runnable sizeReady = this::sizeReady;
protected final Runnable messageReady = this::messageReady;
protected Encoder(Errno errno, int bufsize)
{
super(errno, bufsize);
}
protected abstract void sizeReady();
protected abstract void messageReady();
}
jeromq-0.6.0/src/main/java/zmq/io/coder/EncoderBase.java 0000664 0000000 0000000 00000012636 14557711263 0023020 0 ustar 00root root 0000000 0000000 package zmq.io.coder;
import java.nio.ByteBuffer;
import zmq.Msg;
import zmq.util.Errno;
import zmq.util.ValueReference;
public abstract class EncoderBase implements IEncoder
{
// Where to get the data to write from.
private ByteBuffer writeBuf;
// Next step. If set to null, it means that associated data stream
// is dead.
private Runnable next;
// If true, first byte of the message is being written.
private boolean newMsgFlag;
// How much data to write before next step should be executed.
private int toWrite;
// The buffer for encoded data.
private final ByteBuffer buffer;
private final int bufferSize;
private boolean error;
protected Msg inProgress;
private final Errno errno;
protected EncoderBase(Errno errno, int bufferSize)
{
this.errno = errno;
this.bufferSize = bufferSize;
buffer = ByteBuffer.allocateDirect(bufferSize);
error = false;
}
// Load a new message into encoder.
@Override
public final void loadMsg(Msg msg)
{
assert (inProgress == null);
inProgress = msg;
next();
}
// The function returns a batch of binary data. The data
// are filled to a supplied buffer. If no buffer is supplied (data
// is NULL) encoder will provide buffer of its own.
@Override
public final int encode(ValueReference data, int size)
{
int bufferSize = size;
ByteBuffer buf = data.get();
if (buf == null) {
buf = this.buffer;
bufferSize = this.bufferSize;
buffer.clear();
}
if (inProgress == null) {
return 0;
}
int pos = 0;
buf.limit(buf.capacity());
while (pos < bufferSize) {
// If there are no more data to return, run the state machine.
// If there are still no data, return what we already have
// in the buffer.
if (toWrite == 0) {
if (newMsgFlag) {
inProgress = null;
break;
}
next();
}
// If there are no data in the buffer yet and we are able to
// fill whole buffer in a single go, let's use zero-copy.
// There's no disadvantage to it as we cannot stuck multiple
// messages into the buffer anyway. Note that subsequent
// write(s) are non-blocking, thus each single write writes
// at most SO_SNDBUF bytes at once not depending on how large
// is the chunk returned from here.
// As a consequence, large messages being sent won't block
// other engines running in the same I/O thread for excessive
// amounts of time.
if (pos == 0 && data.get() == null && toWrite >= bufferSize) {
writeBuf.limit(writeBuf.capacity());
data.set(writeBuf);
pos = toWrite;
writeBuf = null;
toWrite = 0;
return pos;
}
// Copy data to the buffer. If the buffer is full, return.
int toCopy = Math.min(toWrite, bufferSize - pos);
int limit = writeBuf.limit();
writeBuf.limit(Math.min(writeBuf.capacity(), writeBuf.position() + toCopy));
int current = buf.position();
buf.put(writeBuf);
toCopy = buf.position() - current;
writeBuf.limit(limit);
pos += toCopy;
toWrite -= toCopy;
}
data.set(buf);
return pos;
}
@Override
public void encoded()
{
buffer.flip();
}
protected void encodingError()
{
error = true;
}
public final boolean isError()
{
return error;
}
protected void next()
{
if (next != null) {
next.run();
}
}
protected void nextStep(Msg msg, Runnable state, boolean beginning)
{
if (msg == null) {
nextStep((byte[]) null, 0, state, beginning);
}
else {
nextStep(msg.buf(), state, beginning);
}
}
// This function should be called from derived class to write the data
// to the buffer and schedule next state machine action.
private void nextStep(byte[] buf, int toWrite, Runnable next, boolean newMsgFlag)
{
if (buf != null) {
writeBuf = ByteBuffer.wrap(buf);
writeBuf.limit(toWrite);
}
else {
writeBuf = null;
}
this.toWrite = toWrite;
this.next = next;
this.newMsgFlag = newMsgFlag;
}
protected void initStep(Runnable next, boolean newMsgFlag)
{
nextStep((byte[]) null, 0, next, newMsgFlag);
}
private void nextStep(ByteBuffer buf, Runnable next, boolean newMsgFlag)
{
nextStep(buf, buf.limit(), next, newMsgFlag);
}
protected void nextStep(ByteBuffer buf, int toWrite, Runnable next, boolean newMsgFlag)
{
buf.limit(toWrite);
buf.position(toWrite);
buf.flip();
writeBuf = buf;
this.toWrite = toWrite;
this.next = next;
this.newMsgFlag = newMsgFlag;
}
public int errno()
{
return errno.get();
}
public void errno(int err)
{
this.errno.set(err);
}
@Override
public void destroy()
{
}
}
jeromq-0.6.0/src/main/java/zmq/io/coder/IDecoder.java 0000664 0000000 0000000 00000000675 14557711263 0022324 0 ustar 00root root 0000000 0000000 package zmq.io.coder;
import java.nio.ByteBuffer;
import zmq.Msg;
import zmq.util.ValueReference;
public interface IDecoder
{
interface Step
{
enum Result
{
MORE_DATA,
DECODED,
ERROR;
}
Result apply();
}
ByteBuffer getBuffer();
Step.Result decode(ByteBuffer buffer, int size, ValueReference processed);
Msg msg();
void destroy();
}
jeromq-0.6.0/src/main/java/zmq/io/coder/IEncoder.java 0000664 0000000 0000000 00000001155 14557711263 0022330 0 ustar 00root root 0000000 0000000 package zmq.io.coder;
import java.nio.ByteBuffer;
import zmq.Msg;
import zmq.util.ValueReference;
public interface IEncoder
{
// Load a new message into encoder.
void loadMsg(Msg msg);
// The function returns a batch of binary data. The data
// are filled to a supplied buffer. If no buffer is supplied (data_
// points to NULL) decoder object will provide buffer of its own.
int encode(ValueReference data, int size);
void destroy();
// called when stream engine finished encoding all messages and is ready to
// send data to network layer
void encoded();
}
jeromq-0.6.0/src/main/java/zmq/io/coder/raw/ 0000775 0000000 0000000 00000000000 14557711263 0020564 5 ustar 00root root 0000000 0000000 jeromq-0.6.0/src/main/java/zmq/io/coder/raw/RawDecoder.java 0000664 0000000 0000000 00000001622 14557711263 0023447 0 ustar 00root root 0000000 0000000 package zmq.io.coder.raw;
import java.nio.ByteBuffer;
import zmq.Msg;
import zmq.io.coder.IDecoder;
import zmq.util.ValueReference;
public class RawDecoder implements IDecoder
{
// The buffer for data to decode.
private final ByteBuffer buffer;
protected Msg inProgress;
public RawDecoder(int bufsize)
{
buffer = ByteBuffer.allocateDirect(bufsize);
inProgress = new Msg();
}
@Override
public ByteBuffer getBuffer()
{
buffer.clear();
return buffer;
}
@Override
public Step.Result decode(ByteBuffer buffer, int size, ValueReference processed)
{
processed.set(size);
inProgress = new Msg(size);
inProgress.put(buffer);
return Step.Result.DECODED;
}
@Override
public Msg msg()
{
return inProgress;
}
@Override
public void destroy()
{
}
}
jeromq-0.6.0/src/main/java/zmq/io/coder/raw/RawEncoder.java 0000664 0000000 0000000 00000001203 14557711263 0023454 0 ustar 00root root 0000000 0000000 package zmq.io.coder.raw;
import zmq.io.coder.Encoder;
import zmq.util.Errno;
// Encoder for 0MQ framing protocol. Converts messages into data batches.
public class RawEncoder extends Encoder
{
public RawEncoder(Errno errno, int bufsize)
{
super(errno, bufsize);
// Write 0 bytes to the batch and go to messageReady state.
initStep(messageReady, true);
}
@Override
protected void sizeReady()
{
throw new UnsupportedOperationException();
}
@Override
protected void messageReady()
{
nextStep(inProgress.buf(), inProgress.size(), messageReady, true);
}
}
jeromq-0.6.0/src/main/java/zmq/io/coder/v1/ 0000775 0000000 0000000 00000000000 14557711263 0020321 5 ustar 00root root 0000000 0000000 jeromq-0.6.0/src/main/java/zmq/io/coder/v1/V1Decoder.java 0000664 0000000 0000000 00000005532 14557711263 0022745 0 ustar 00root root 0000000 0000000 package zmq.io.coder.v1;
import java.nio.ByteBuffer;
import zmq.Msg;
import zmq.ZError;
import zmq.io.coder.Decoder;
import zmq.msg.MsgAllocator;
import zmq.util.Errno;
import zmq.util.Wire;
public class V1Decoder extends Decoder
{
private final ByteBuffer tmpbuf;
public V1Decoder(Errno errno, int bufsize, long maxmsgsize, MsgAllocator allocator)
{
super(errno, bufsize, maxmsgsize, allocator);
tmpbuf = ByteBuffer.allocate(8);
tmpbuf.limit(1);
// At the beginning, read one byte and go to ONE_BYTE_SIZE_READY state.
nextStep(tmpbuf, oneByteSizeReady);
}
@Override
protected Step.Result oneByteSizeReady()
{
// First byte of size is read. If it is 0xff read 8-byte size.
// Otherwise allocate the buffer for message data and read the
// message data into it.
int size = tmpbuf.get(0) & 0xff;
if (size == 0xff) {
tmpbuf.position(0);
tmpbuf.limit(8);
nextStep(tmpbuf, eightByteSizeReady);
}
else {
// There has to be at least one byte (the flags) in the message).
if (size == 0) {
errno(ZError.EPROTO);
return Step.Result.ERROR;
}
tmpbuf.position(0);
tmpbuf.limit(1);
Step.Result rc = sizeReady(size - 1);
if (rc != Step.Result.ERROR) {
nextStep(tmpbuf, flagsReady);
}
return rc;
}
return Step.Result.MORE_DATA;
}
@Override
protected Step.Result eightByteSizeReady()
{
// 8-byte payload length is read. Allocate the buffer
// for message body and read the message data into it.
tmpbuf.position(0);
tmpbuf.limit(8);
final long payloadLength = Wire.getUInt64(tmpbuf, 0);
if (payloadLength <= 0) {
errno(ZError.EPROTO);
return Step.Result.ERROR;
}
tmpbuf.limit(1);
Step.Result rc = sizeReady(payloadLength - 1);
if (rc != Step.Result.ERROR) {
nextStep(tmpbuf, flagsReady);
}
return rc;
}
@Override
protected Step.Result flagsReady()
{
// Store the flags from the wire into the message structure.
int first = tmpbuf.get(0) & 0xff;
if ((first & V1Protocol.MORE_FLAG) > 0) {
inProgress.setFlags(Msg.MORE);
}
nextStep(inProgress, messageReady);
return Step.Result.MORE_DATA;
}
@Override
protected Step.Result messageReady()
{
// Message is completely read. Push it further and start reading
// new message. (inProgress is a 0-byte message after this point.)
tmpbuf.position(0);
tmpbuf.limit(1);
nextStep(tmpbuf, oneByteSizeReady);
return Step.Result.DECODED;
}
}
jeromq-0.6.0/src/main/java/zmq/io/coder/v1/V1Encoder.java 0000664 0000000 0000000 00000003041 14557711263 0022750 0 ustar 00root root 0000000 0000000 package zmq.io.coder.v1;
import java.nio.ByteBuffer;
import zmq.Msg;
import zmq.io.coder.Encoder;
import zmq.util.Errno;
import zmq.util.Wire;
// Encoder for 0MQ framing protocol. Converts messages into data stream.
public class V1Encoder extends Encoder
{
private final ByteBuffer tmpbufWrap;
public V1Encoder(Errno errno, int bufsize)
{
super(errno, bufsize);
tmpbufWrap = ByteBuffer.allocate(10);
// Write 0 bytes to the batch and go to messageReady state.
initStep(messageReady, true);
}
@Override
protected void sizeReady()
{
// Write message body into the buffer.
nextStep(inProgress.buf(), inProgress.size(), messageReady, true);
}
@Override
protected void messageReady()
{
// Get the message size.
int size = inProgress.size();
// Account for the 'flags' byte.
size++;
// For messages less than 255 bytes long, write one byte of message size.
// For longer messages write 0xff escape character followed by 8-byte
// message size. In both cases 'flags' field follows.
tmpbufWrap.position(0);
if (size < 255) {
tmpbufWrap.limit(2);
tmpbufWrap.put((byte) size);
}
else {
tmpbufWrap.limit(10);
tmpbufWrap.put((byte) 0xff);
Wire.putUInt64(tmpbufWrap, size);
}
tmpbufWrap.put((byte) (inProgress.flags() & Msg.MORE));
nextStep(tmpbufWrap, tmpbufWrap.limit(), sizeReady, false);
}
}
jeromq-0.6.0/src/main/java/zmq/io/coder/v1/V1Protocol.java 0000664 0000000 0000000 00000000272 14557711263 0023175 0 ustar 00root root 0000000 0000000 package zmq.io.coder.v1;
public interface V1Protocol
{
int VERSION = 1;
int MORE_FLAG = 1;
// make checkstyle not block the release
@Override
String toString();
}
jeromq-0.6.0/src/main/java/zmq/io/coder/v2/ 0000775 0000000 0000000 00000000000 14557711263 0020322 5 ustar 00root root 0000000 0000000 jeromq-0.6.0/src/main/java/zmq/io/coder/v2/V2Decoder.java 0000664 0000000 0000000 00000005137 14557711263 0022750 0 ustar 00root root 0000000 0000000 package zmq.io.coder.v2;
import java.nio.ByteBuffer;
import zmq.Msg;
import zmq.io.coder.Decoder;
import zmq.msg.MsgAllocator;
import zmq.util.Errno;
import zmq.util.Wire;
public class V2Decoder extends Decoder
{
private final ByteBuffer tmpbuf;
private int msgFlags;
public V2Decoder(Errno errno, int bufsize, long maxmsgsize, MsgAllocator allocator)
{
super(errno, bufsize, maxmsgsize, allocator);
tmpbuf = ByteBuffer.allocate(8);
tmpbuf.limit(1);
// At the beginning, read one byte and go to ONE_BYTE_SIZE_READY state.
nextStep(tmpbuf, flagsReady);
}
@Override
protected Msg allocate(int size)
{
Msg msg = super.allocate(size);
msg.setFlags(msgFlags);
return msg;
}
@Override
protected Step.Result oneByteSizeReady()
{
int size = tmpbuf.get(0) & 0xff;
Step.Result rc = sizeReady(size);
if (rc != Step.Result.ERROR) {
nextStep(inProgress, messageReady);
}
return rc;
}
@Override
protected Step.Result eightByteSizeReady()
{
// The payload size is encoded as 64-bit unsigned integer.
// The most significant byte comes first.
tmpbuf.position(0);
tmpbuf.limit(8);
final long size = Wire.getUInt64(tmpbuf, 0);
Step.Result rc = sizeReady(size);
if (rc != Step.Result.ERROR) {
nextStep(inProgress, messageReady);
}
return rc;
}
@Override
protected Step.Result flagsReady()
{
// Store the flags from the wire into the message structure.
this.msgFlags = 0;
int first = tmpbuf.get(0) & 0xff;
if ((first & V2Protocol.MORE_FLAG) > 0) {
this.msgFlags |= Msg.MORE;
}
if ((first & V2Protocol.COMMAND_FLAG) > 0) {
this.msgFlags |= Msg.COMMAND;
}
// The payload length is either one or eight bytes,
// depending on whether the 'large' bit is set.
tmpbuf.position(0);
if ((first & V2Protocol.LARGE_FLAG) > 0) {
tmpbuf.limit(8);
nextStep(tmpbuf, eightByteSizeReady);
}
else {
tmpbuf.limit(1);
nextStep(tmpbuf, oneByteSizeReady);
}
return Step.Result.MORE_DATA;
}
@Override
protected Step.Result messageReady()
{
// Message is completely read. Signal this to the caller
// and prepare to decode next message.
tmpbuf.position(0);
tmpbuf.limit(1);
nextStep(tmpbuf, flagsReady);
return Step.Result.DECODED;
}
}
jeromq-0.6.0/src/main/java/zmq/io/coder/v2/V2Encoder.java 0000664 0000000 0000000 00000003332 14557711263 0022755 0 ustar 00root root 0000000 0000000 package zmq.io.coder.v2;
import java.nio.ByteBuffer;
import zmq.io.coder.Encoder;
import zmq.util.Errno;
import zmq.util.Wire;
// Encoder for 0MQ framing protocol. Converts messages into data stream.
public class V2Encoder extends Encoder
{
private final ByteBuffer tmpbufWrap;
public V2Encoder(Errno errno, int bufsize)
{
super(errno, bufsize);
tmpbufWrap = ByteBuffer.allocate(9);
// Write 0 bytes to the batch and go to messageReady state.
initStep(messageReady, true);
}
@Override
protected void messageReady()
{
// Encode flags.
byte protocolFlags = 0;
if (inProgress.hasMore()) {
protocolFlags |= V2Protocol.MORE_FLAG;
}
if (inProgress.size() > 255) {
protocolFlags |= V2Protocol.LARGE_FLAG;
}
if (inProgress.isCommand()) {
protocolFlags |= V2Protocol.COMMAND_FLAG;
}
// Encode the message length. For messages less then 256 bytes,
// the length is encoded as 8-bit unsigned integer. For larger
// messages, 64-bit unsigned integer in network byte order is used.
final int size = inProgress.size();
tmpbufWrap.position(0);
tmpbufWrap.put(protocolFlags);
if (size > 255) {
tmpbufWrap.limit(9);
Wire.putUInt64(tmpbufWrap, size);
}
else {
tmpbufWrap.limit(2);
tmpbufWrap.put((byte) size);
}
nextStep(tmpbufWrap, tmpbufWrap.limit(), sizeReady, false);
}
@Override
protected void sizeReady()
{
// Write message body into the buffer.
nextStep(inProgress.buf(), inProgress.size(), messageReady, true);
}
}
jeromq-0.6.0/src/main/java/zmq/io/coder/v2/V2Protocol.java 0000664 0000000 0000000 00000000416 14557711263 0023177 0 ustar 00root root 0000000 0000000 package zmq.io.coder.v2;
import zmq.io.coder.v1.V1Protocol;
public interface V2Protocol extends V1Protocol
{
int VERSION = 2;
int LARGE_FLAG = 2;
int COMMAND_FLAG = 4;
// make checkstyle not block the release
@Override
String toString();
}
jeromq-0.6.0/src/main/java/zmq/io/mechanism/ 0000775 0000000 0000000 00000000000 14557711263 0020643 5 ustar 00root root 0000000 0000000 jeromq-0.6.0/src/main/java/zmq/io/mechanism/Mechanism.java 0000664 0000000 0000000 00000025527 14557711263 0023425 0 ustar 00root root 0000000 0000000 package zmq.io.mechanism;
import static zmq.io.Metadata.IDENTITY;
import static zmq.io.Metadata.SOCKET_TYPE;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import zmq.Msg;
import zmq.Options;
import zmq.ZError;
import zmq.ZMQ;
import zmq.io.Metadata;
import zmq.io.Msgs;
import zmq.io.SessionBase;
import zmq.io.net.Address;
import zmq.socket.Sockets;
import zmq.util.Blob;
import zmq.util.Wire;
// Abstract class representing security mechanism.
// Different mechanism extends this class.
public abstract class Mechanism
{
public enum Status
{
HANDSHAKING,
READY,
ERROR
}
protected final Options options;
private Blob identity;
private Blob userId;
// Properties received from ZAP server.
public final Metadata zapProperties = new Metadata();
// Properties received from ZMTP peer.
public final Metadata zmtpProperties = new Metadata();
protected final SessionBase session;
private final Address peerAddress;
protected String statusCode;
protected Mechanism(SessionBase session, Address peerAddress, Options options)
{
this.session = session;
this.options = options;
this.peerAddress = peerAddress;
}
public abstract Status status();
private void setPeerIdentity(byte[] data)
{
identity = Blob.createBlob(data);
}
public final Msg peerIdentity()
{
byte[] data = new byte[0];
int size = 0;
if (identity != null) {
data = identity.data();
size = identity.size();
}
Msg msg = new Msg(size);
msg.put(data, 0, size);
msg.setFlags(Msg.IDENTITY);
return msg;
}
private void setUserId(byte[] data)
{
userId = Blob.createBlob(data);
zapProperties.put(Metadata.USER_ID, new String(data, ZMQ.CHARSET));
}
public final Blob getUserId()
{
return userId;
}
protected final void addProperty(ByteBuffer buf, String name, String value)
{
addProperty(buf, name, value.getBytes(ZMQ.CHARSET));
}
protected final void addProperty(Msg msg, String name, String value)
{
addProperty(msg, name, value.getBytes(ZMQ.CHARSET));
}
protected final void addProperty(ByteBuffer buf, String name, byte[] value)
{
byte[] nameB = name.getBytes(ZMQ.CHARSET);
int nameLength = nameB.length;
assert (nameLength <= 255);
int valueLength = value == null ? 0 : value.length;
buf.put((byte) nameLength);
buf.put(nameB);
Wire.putUInt32(buf, valueLength);
if (value != null) {
buf.put(value);
}
}
protected final void addProperty(Msg msg, String name, byte[] value)
{
byte[] nameB = name.getBytes(ZMQ.CHARSET);
int nameLength = nameB.length;
assert (nameLength <= 255);
int valueLength = value == null ? 0 : value.length;
msg.put((byte) nameLength);
msg.put(nameB);
Wire.putUInt32(msg, valueLength);
if (value != null) {
msg.put(value);
}
}
protected final int parseMetadata(Msg msg, int offset, boolean zapFlag)
{
return parseMetadata(msg.buf(), offset, zapFlag);
}
protected final int parseMetadata(ByteBuffer msg, int offset, boolean zapFlag)
{
Metadata meta = zapFlag ? zapProperties : zmtpProperties;
return meta.read(msg, offset, (name, value, valueAsString) -> {
int type = options.asType != -1 ? options.asType : options.type;
if (IDENTITY.equals(name) && options.recvIdentity) {
setPeerIdentity(value);
}
else if (SOCKET_TYPE.equals(name)) {
if (!Sockets.compatible(type, valueAsString)) {
return ZError.EINVAL;
}
}
else {
int rc = property(name, value);
if (rc == -1) {
return -1;
}
}
// continue
return 0;
});
}
protected int property(String name, byte[] value)
{
// Default implementation does not check
// property values and returns 0 to signal success.
return 0;
}
protected final String socketType()
{
if (options.asType != -1) {
return Sockets.name(options.asType);
}
else {
return Sockets.name(options.type);
}
}
protected boolean compare(Msg msg, String data, boolean includeLength)
{
return Msgs.startsWith(msg, data, includeLength);
}
protected boolean compare(ByteBuffer a1, byte[] b, int offset, int length)
{
if (length > b.length) {
return false;
}
boolean comparison = true;
for (int idx = 0; idx < length; ++idx) {
comparison = a1.get(idx + offset) == b[idx];
if (!comparison) {
break;
}
}
return comparison;
}
public Msg decode(Msg msg)
{
return msg;
}
public Msg encode(Msg msg)
{
return msg;
}
public abstract int zapMsgAvailable();
public abstract int processHandshakeCommand(Msg msg);
public abstract int nextHandshakeCommand(Msg msg);
protected int parseErrorMessage(Msg msg)
{
if (msg.size() < 7 && msg.size() != 6) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR);
return ZError.EPROTO;
}
if (msg.size() >= 7) {
byte errorReasonLength = msg.get(6);
if (errorReasonLength > msg.size() - 7) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR);
return ZError.EPROTO;
}
// triple digit status code
if (msg.size() == 10) {
byte[] statusBuffer = Arrays.copyOfRange(msg.data(), 7, 10);
String statusCode = new String(statusBuffer, ZMQ.CHARSET);
if (handleErrorReason(statusCode) < 0) {
return ZError.EPROTO;
}
}
}
return 0;
}
protected int handleErrorReason(String reason)
{
int rc = -1;
if (reason.length() == 3
&& reason.charAt(1) == '0'
&& reason.charAt(2) == '0'
&& reason.charAt(0) >= '3'
&& reason.charAt(0) <= '5') {
try {
int statusCode = Integer.parseInt(reason);
session.getSocket().eventHandshakeFailedAuth(session.getEndpoint(), statusCode);
rc = 0;
}
catch (NumberFormatException e) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_MALFORMED_REPLY);
}
}
else {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_MALFORMED_REPLY);
}
return rc;
}
protected final void sendZapRequest(Mechanisms mechanism, boolean more)
{
assert (session != null);
assert (peerAddress != null);
assert (mechanism != null);
Msg msg = new Msg();
// Address delimiter frame
msg.setFlags(Msg.MORE);
boolean rc = session.writeZapMsg(msg);
assert (rc);
// Version frame
msg = new Msg(3);
msg.setFlags(Msg.MORE);
msg.put("1.0".getBytes(ZMQ.CHARSET));
rc = session.writeZapMsg(msg);
assert (rc);
// Request id frame
msg = new Msg(1);
msg.setFlags(Msg.MORE);
msg.put("1".getBytes(ZMQ.CHARSET));
rc = session.writeZapMsg(msg);
assert (rc);
// Domain frame
msg = new Msg(options.zapDomain.length());
msg.setFlags(Msg.MORE);
msg.put(options.zapDomain.getBytes(ZMQ.CHARSET));
rc = session.writeZapMsg(msg);
assert (rc);
// Address frame
byte[] host = peerAddress.host().getBytes(ZMQ.CHARSET);
msg = new Msg(host.length);
msg.setFlags(Msg.MORE);
msg.put(host);
rc = session.writeZapMsg(msg);
assert (rc);
// Identity frame
msg = new Msg(options.identitySize);
msg.setFlags(Msg.MORE);
msg.put(options.identity, 0, options.identitySize);
rc = session.writeZapMsg(msg);
assert (rc);
// Mechanism frame
msg = new Msg(mechanism.name().length());
msg.put(mechanism.name().getBytes(ZMQ.CHARSET));
if (more) {
msg.setFlags(Msg.MORE);
}
rc = session.writeZapMsg(msg);
assert (rc);
}
protected final int receiveAndProcessZapReply()
{
assert (session != null);
List msgs = new ArrayList<>(7); // ZAP reply consists of 7 frames
// Initialize all reply frames
for (int idx = 0; idx < 7; ++idx) {
Msg msg = session.readZapMsg();
if (msg == null) {
return session.errno.get();
}
if ((msg.flags() & Msg.MORE) == (idx < 6 ? 0 : Msg.MORE)) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_MALFORMED_REPLY);
return ZError.EPROTO;
}
msgs.add(msg);
}
// Address delimiter frame
if (msgs.get(0).size() > 0) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_UNSPECIFIED);
return ZError.EPROTO;
}
// Version frame
if (msgs.get(1).size() != 3 || !compare(msgs.get(1), "1.0", false)) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_BAD_VERSION);
return ZError.EPROTO;
}
// Request id frame
if (msgs.get(2).size() != 1 || !compare(msgs.get(2), "1", false)) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_BAD_REQUEST_ID);
return ZError.EPROTO;
}
// Status code frame
if (msgs.get(3).size() != 3) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_INVALID_STATUS_CODE);
return ZError.EPROTO;
}
// Save status code
statusCode = new String(msgs.get(3).data(), ZMQ.CHARSET);
// Save user id
setUserId(msgs.get(5).data());
// Process metadata frame
return parseMetadata(msgs.get(6), 0, true);
}
public void destroy()
{
}
}
jeromq-0.6.0/src/main/java/zmq/io/mechanism/Mechanisms.java 0000664 0000000 0000000 00000010200 14557711263 0023566 0 ustar 00root root 0000000 0000000 package zmq.io.mechanism;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import zmq.Options;
import zmq.ZMQ;
import zmq.io.SessionBase;
import zmq.io.mechanism.curve.CurveClientMechanism;
import zmq.io.mechanism.curve.CurveServerMechanism;
import zmq.io.mechanism.gssapi.GssapiClientMechanism;
import zmq.io.mechanism.gssapi.GssapiServerMechanism;
import zmq.io.mechanism.plain.PlainClientMechanism;
import zmq.io.mechanism.plain.PlainServerMechanism;
import zmq.io.net.Address;
public enum Mechanisms
{
NULL {
@Override
public void check(Options options)
{
// Nothing to check
}
@Override
public Mechanism create(SessionBase session, Address peerAddress, Options options)
{
return new NullMechanism(session, peerAddress, options);
}
},
PLAIN {
@Override
public void check(Options options)
{
if (! options.asServer) {
Set errors = new HashSet<>(2);
if (options.plainUsername == null
|| options.plainUsername.length() >= 256) {
errors.add("user name invalid");
}
if (options.plainPassword == null
|| options.plainPassword.length() >= 256) {
errors.add("password is invalid");
}
if (!errors.isEmpty()) {
throw new IllegalStateException("Plain mechanism definition incomplete: " + errors);
}
}
}
@Override
public Mechanism create(SessionBase session, Address peerAddress, Options options)
{
if (options.asServer) {
return new PlainServerMechanism(session, peerAddress, options);
}
else {
return new PlainClientMechanism(session, options);
}
}
},
CURVE {
@Override
public void check(Options options)
{
Set errors = new HashSet<>(3);
if (options.curvePublicKey == null || options.curvePublicKey.length != Options.CURVE_KEYSIZE) {
errors.add("public key is invalid");
}
if (options.curveSecretKey == null || options.curveSecretKey.length != Options.CURVE_KEYSIZE) {
errors.add("secret key is invalid");
}
if (!options.asServer && (options.curveServerKey == null || options.curveServerKey.length != Options.CURVE_KEYSIZE)) {
errors.add("not a server and no server public key given");
}
if (!errors.isEmpty()) {
throw new IllegalStateException("Curve mechanism definition incomplete: " + errors);
}
}
@Override
public Mechanism create(SessionBase session, Address peerAddress, Options options)
{
if (options.asServer) {
return new CurveServerMechanism(session, peerAddress, options);
}
else {
return new CurveClientMechanism(session, options);
}
}
},
GSSAPI {
@Override
public void check(Options options)
{
throw new UnsupportedOperationException("GSSAPI mechanism is not yet implemented");
}
@Override
public Mechanism create(SessionBase session, Address peerAddress, Options options)
{
if (options.asServer) {
return new GssapiServerMechanism(session, peerAddress, options);
}
else {
return new GssapiClientMechanism(session, options);
}
}
};
public abstract Mechanism create(SessionBase session, Address peerAddress, Options options);
public abstract void check(Options options);
public boolean isMechanism(ByteBuffer greetingRecv)
{
byte[] dst = new byte[20];
greetingRecv.get(dst, 0, dst.length);
byte[] name = name().getBytes(ZMQ.CHARSET);
byte[] comp = Arrays.copyOf(name, 20);
return Arrays.equals(dst, comp);
}
}
jeromq-0.6.0/src/main/java/zmq/io/mechanism/NullMechanism.java 0000664 0000000 0000000 00000010016 14557711263 0024243 0 ustar 00root root 0000000 0000000 package zmq.io.mechanism;
import static zmq.io.Metadata.IDENTITY;
import static zmq.io.Metadata.SOCKET_TYPE;
import zmq.Msg;
import zmq.Options;
import zmq.ZError;
import zmq.ZMQ;
import zmq.io.SessionBase;
import zmq.io.net.Address;
class NullMechanism extends Mechanism
{
private static final String OK = "200";
private static final String READY = "READY";
private static final String ERROR = "ERROR";
private boolean readyCommandSent;
private boolean errorCommandSent;
private boolean readyCommandReceived;
private boolean errorCommandReceived;
private boolean zapConnected;
private boolean zapRequestSent;
private boolean zapReplyReceived;
NullMechanism(SessionBase session, Address peerAddress, Options options)
{
super(session, peerAddress, options);
// NULL mechanism only uses ZAP if there's a domain defined
// This prevents ZAP requests on naive sockets
if (options.zapDomain != null && !options.zapDomain.isEmpty() && session.zapConnect() == 0) {
zapConnected = true;
}
}
@Override
public int nextHandshakeCommand(Msg msg)
{
if (readyCommandSent || errorCommandSent) {
return ZError.EAGAIN;
}
if (zapConnected && !zapReplyReceived) {
if (zapRequestSent) {
return ZError.EAGAIN;
}
sendZapRequest(Mechanisms.NULL, false);
zapRequestSent = true;
int rc = receiveAndProcessZapReply();
if (rc != 0) {
return rc;
}
zapReplyReceived = true;
}
if (zapReplyReceived && !OK.equals(statusCode)) {
msg.putShortString(ERROR);
msg.putShortString(statusCode);
errorCommandSent = true;
return 0;
}
// Add mechanism string
msg.putShortString(READY);
// Add socket type property
String socketType = socketType();
addProperty(msg, SOCKET_TYPE, socketType);
// Add identity property
if (options.type == ZMQ.ZMQ_REQ || options.type == ZMQ.ZMQ_DEALER || options.type == ZMQ.ZMQ_ROUTER) {
addProperty(msg, IDENTITY, options.identity);
}
readyCommandSent = true;
return 0;
}
@Override
public int processHandshakeCommand(Msg msg)
{
if (readyCommandReceived || errorCommandReceived) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
return ZError.EPROTO;
}
int dataSize = msg.size();
int rc;
if (dataSize >= 6 && compare(msg, READY, true)) {
rc = processReadyCommand(msg);
}
else if (dataSize >= 6 && compare(msg, ERROR, true)) {
rc = processErrorCommand(msg);
}
else {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
return ZError.EPROTO;
}
return rc;
}
private int processReadyCommand(Msg msg)
{
readyCommandReceived = true;
return parseMetadata(msg, 6, false);
}
private int processErrorCommand(Msg msg)
{
errorCommandReceived = true;
return parseErrorMessage(msg);
}
@Override
public int zapMsgAvailable()
{
if (zapReplyReceived) {
return ZError.EFSM;
}
int rc = receiveAndProcessZapReply();
if (rc == 0) {
zapReplyReceived = true;
}
return rc;
}
@Override
public Status status()
{
boolean commandSent = readyCommandSent || errorCommandSent;
boolean commandReceived = readyCommandReceived || errorCommandReceived;
if (readyCommandSent && readyCommandReceived) {
return Status.READY;
}
if (commandSent && commandReceived) {
return Status.ERROR;
}
return Status.HANDSHAKING;
}
}
jeromq-0.6.0/src/main/java/zmq/io/mechanism/curve/ 0000775 0000000 0000000 00000000000 14557711263 0021767 5 ustar 00root root 0000000 0000000 jeromq-0.6.0/src/main/java/zmq/io/mechanism/curve/Curve.java 0000664 0000000 0000000 00000014015 14557711263 0023717 0 ustar 00root root 0000000 0000000 package zmq.io.mechanism.curve;
import java.nio.ByteBuffer;
import com.neilalexander.jnacl.crypto.curve25519xsalsa20poly1305;
import com.neilalexander.jnacl.crypto.xsalsa20poly1305;
import zmq.util.Utils;
import zmq.util.Z85;
// wrapper around the wrapper of libsodium (ahem), for shorter names.
public class Curve
{
enum Size
{
NONCE {
@Override
public int bytes()
{
return curve25519xsalsa20poly1305.crypto_secretbox_NONCEBYTES;
}
},
ZERO {
@Override
public int bytes()
{
return curve25519xsalsa20poly1305.crypto_secretbox_ZEROBYTES;
}
},
BOXZERO {
@Override
public int bytes()
{
return curve25519xsalsa20poly1305.crypto_secretbox_BOXZEROBYTES;
}
},
PUBLICKEY {
@Override
public int bytes()
{
return curve25519xsalsa20poly1305.crypto_secretbox_PUBLICKEYBYTES;
}
},
SECRETKEY {
@Override
public int bytes()
{
return curve25519xsalsa20poly1305.crypto_secretbox_SECRETKEYBYTES;
}
},
KEY {
@Override
public int bytes()
{
return 32;
}
},
BEFORENM {
@Override
public int bytes()
{
return curve25519xsalsa20poly1305.crypto_secretbox_BEFORENMBYTES;
}
};
public abstract int bytes();
}
public Curve()
{
}
public static String z85EncodePublic(byte[] publicKey)
{
return Z85.encode(publicKey, Size.PUBLICKEY.bytes());
}
/**
* Generates a pair of Z85-encoded keys for use with this class.
*
* @return an array of 2 strings, holding Z85-encoded keys.
* The first element of the array is the public key,
* the second element is the private (or secret) key.
*/
public String[] keypairZ85()
{
String[] pair = new String[2];
byte[] publicKey = new byte[Size.PUBLICKEY.bytes()];
byte[] secretKey = new byte[Size.SECRETKEY.bytes()];
int rc = curve25519xsalsa20poly1305.crypto_box_keypair(publicKey, secretKey);
assert (rc == 0);
pair[0] = Z85.encode(publicKey, Size.PUBLICKEY.bytes());
pair[1] = Z85.encode(secretKey, Size.SECRETKEY.bytes());
return pair;
}
/**
* Generates a pair of keys for use with this class.
*
* @return an array of 2 byte arrays, holding keys.
* The first element of the array is the public key,
* the second element is the private (or secret) key.
*/
public byte[][] keypair()
{
byte[][] pair = new byte[2][];
byte[] publicKey = new byte[Size.PUBLICKEY.bytes()];
byte[] secretKey = new byte[Size.SECRETKEY.bytes()];
int rc = curve25519xsalsa20poly1305.crypto_box_keypair(publicKey, secretKey);
assert (rc == 0);
pair[0] = publicKey;
pair[1] = secretKey;
return pair;
}
int beforenm(byte[] outSharedKey, byte[] publicKey, byte[] secretKey)
{
return curve25519xsalsa20poly1305.crypto_box_beforenm(outSharedKey, publicKey, secretKey);
}
int afternm(ByteBuffer ciphered, ByteBuffer plaintext, int length, ByteBuffer nonce, byte[] precom)
{
return afternm(ciphered.array(), plaintext.array(), length, nonce.array(), precom);
}
int afternm(byte[] ciphered, byte[] plaintext, int length, byte[] nonce, byte[] precomp)
{
return curve25519xsalsa20poly1305.crypto_box_afternm(ciphered, plaintext, length, nonce, precomp);
}
int openAfternm(ByteBuffer plaintext, ByteBuffer messagebox, int length, ByteBuffer nonce, byte[] precom)
{
return openAfternm(plaintext.array(), messagebox.array(), length, nonce.array(), precom);
}
int openAfternm(byte[] plaintext, byte[] cipher, int length, byte[] nonce, byte[] precom)
{
return curve25519xsalsa20poly1305.crypto_box_open_afternm(plaintext, cipher, length, nonce, precom);
}
int open(ByteBuffer plaintext, ByteBuffer messagebox, int length, ByteBuffer nonce, byte[] precom, byte[] secretKey)
{
return open(plaintext.array(), messagebox.array(), length, nonce.array(), precom, secretKey);
}
int open(byte[] plaintext, byte[] messagebox, int length, byte[] nonce, byte[] publicKey, byte[] secretKey)
{
return curve25519xsalsa20poly1305.crypto_box_open(plaintext, messagebox, length, nonce, publicKey, secretKey);
}
int secretbox(ByteBuffer ciphertext, ByteBuffer plaintext, int length, ByteBuffer nonce, byte[] key)
{
return secretbox(ciphertext.array(), plaintext.array(), length, nonce.array(), key);
}
int secretbox(byte[] ciphertext, byte[] plaintext, int length, byte[] nonce, byte[] key)
{
return xsalsa20poly1305.crypto_secretbox(ciphertext, plaintext, length, nonce, key);
}
int secretboxOpen(ByteBuffer plaintext, ByteBuffer box, int length, ByteBuffer nonce, byte[] key)
{
return secretboxOpen(plaintext.array(), box.array(), length, nonce.array(), key);
}
int secretboxOpen(byte[] plaintext, byte[] box, int length, byte[] nonce, byte[] key)
{
return xsalsa20poly1305.crypto_secretbox_open(plaintext, box, length, nonce, key);
}
byte[] random(int length)
{
return Utils.randomBytes(length);
}
public int box(ByteBuffer ciphertext, ByteBuffer plaintext, int length, ByteBuffer nonce, byte[] publicKey,
byte[] secretKey)
{
return box(ciphertext.array(), plaintext.array(), length, nonce.array(), publicKey, secretKey);
}
public int box(byte[] ciphertext, byte[] plaintext, int length, byte[] nonce, byte[] publicKey, byte[] secretKey)
{
return curve25519xsalsa20poly1305.crypto_box(ciphertext, plaintext, length, nonce, publicKey, secretKey);
}
}
jeromq-0.6.0/src/main/java/zmq/io/mechanism/curve/CurveClientMechanism.java 0000664 0000000 0000000 00000035137 14557711263 0026713 0 ustar 00root root 0000000 0000000 package zmq.io.mechanism.curve;
import static zmq.io.Metadata.IDENTITY;
import static zmq.io.Metadata.SOCKET_TYPE;
import java.nio.ByteBuffer;
import zmq.Msg;
import zmq.Options;
import zmq.ZError;
import zmq.ZMQ;
import zmq.io.SessionBase;
import zmq.io.mechanism.Mechanism;
import zmq.util.Errno;
import zmq.util.Wire;
public class CurveClientMechanism extends Mechanism
{
private enum State
{
SEND_HELLO,
EXPECT_WELCOME,
SEND_INITIATE,
EXPECT_READY,
ERROR_RECEIVED,
CONNECTED
}
private State state;
// Our public key (C)
private final byte[] publicKey;
// Our secret key (c)
private final byte[] secretKey;
// Our short-term public key (C')
private final byte[] cnPublic;
// Our short-term secret key (c')
private final byte[] cnSecret;
// Server's public key (S)
private final byte[] serverKey;
// Server's short-term public key (S')
private final byte[] cnServer = new byte[Curve.Size.PUBLICKEY.bytes()];
// Cookie received from server
private final byte[] cnCookie = new byte[16 + 80];
// Intermediary buffer used to speed up boxing and unboxing.
private final byte[] cnPrecom = new byte[Curve.Size.BEFORENM.bytes()];
private long cnNonce;
private long cnPeerNonce;
private final Curve cryptoBox;
private final Errno errno;
public CurveClientMechanism(SessionBase session, Options options)
{
super(session, null, options);
this.state = State.SEND_HELLO;
cnNonce = 1;
cnPeerNonce = 1;
publicKey = options.curvePublicKey;
assert (publicKey != null && publicKey.length == Curve.Size.PUBLICKEY.bytes());
secretKey = options.curveSecretKey;
assert (secretKey != null && secretKey.length == Curve.Size.SECRETKEY.bytes());
serverKey = options.curveServerKey;
assert (serverKey != null && serverKey.length == Curve.Size.PUBLICKEY.bytes());
cryptoBox = new Curve();
// Generate short-term key pair
byte[][] keys = cryptoBox.keypair();
assert (keys != null && keys.length == 2);
cnPublic = keys[0];
assert (cnPublic != null && cnPublic.length == Curve.Size.PUBLICKEY.bytes());
cnSecret = keys[1];
assert (cnSecret != null && cnSecret.length == Curve.Size.SECRETKEY.bytes());
errno = options.errno;
}
@Override
public int nextHandshakeCommand(Msg msg)
{
int rc;
switch (state) {
case SEND_HELLO:
rc = produceHello(msg);
if (rc == 0) {
state = State.EXPECT_WELCOME;
}
break;
case SEND_INITIATE:
rc = produceInitiate(msg);
if (rc == 0) {
state = State.EXPECT_READY;
}
break;
default:
rc = ZError.EAGAIN;
break;
}
return rc;
}
@Override
public int processHandshakeCommand(Msg msg)
{
int rc;
int dataSize = msg.size();
if (dataSize >= 8 && compare(msg, "WELCOME", true)) {
rc = processWelcome(msg);
}
else if (dataSize >= 6 && compare(msg, "READY", true)) {
rc = processReady(msg);
}
else if (dataSize >= 6 && compare(msg, "ERROR", true)) {
rc = processError(msg);
}
else {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
rc = ZError.EPROTO;
}
return rc;
}
@Override
public Msg encode(Msg msg)
{
assert (state == State.CONNECTED);
byte flags = 0;
if (msg.hasMore()) {
flags |= 0x01;
}
if (msg.isCommand()) {
flags |= 0x02;
}
ByteBuffer messageNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes());
messageNonce.put("CurveZMQMESSAGEC".getBytes(ZMQ.CHARSET));
Wire.putUInt64(messageNonce, cnNonce);
int mlen = Curve.Size.ZERO.bytes() + 1 + msg.size();
ByteBuffer messagePlaintext = ByteBuffer.allocate(mlen);
messagePlaintext.put(Curve.Size.ZERO.bytes(), flags);
messagePlaintext.position(Curve.Size.ZERO.bytes() + 1);
msg.transfer(messagePlaintext, 0, msg.size());
ByteBuffer messageBox = ByteBuffer.allocate(mlen);
int rc = cryptoBox.afternm(messageBox, messagePlaintext, mlen, messageNonce, cnPrecom);
assert (rc == 0);
Msg encoded = new Msg(16 + mlen - Curve.Size.BOXZERO.bytes());
encoded.putShortString("MESSAGE");
encoded.put(messageNonce, 16, 8);
encoded.put(messageBox, Curve.Size.BOXZERO.bytes(), mlen - Curve.Size.BOXZERO.bytes());
cnNonce++;
return encoded;
}
@Override
public Msg decode(Msg msg)
{
assert (state == State.CONNECTED);
if (!compare(msg, "MESSAGE", true)) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
errno.set(ZError.EPROTO);
return null;
}
if (msg.size() < 33) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE);
errno.set(ZError.EPROTO);
return null;
}
ByteBuffer messageNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes());
messageNonce.put("CurveZMQMESSAGES".getBytes(ZMQ.CHARSET));
msg.transfer(messageNonce, 8, 8);
long nonce = msg.getLong(8);
if (nonce <= cnPeerNonce) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
errno.set(ZError.EPROTO);
return null;
}
cnPeerNonce = nonce;
int clen = Curve.Size.BOXZERO.bytes() + msg.size() - 16;
ByteBuffer messagePlaintext = ByteBuffer.allocate(clen);
ByteBuffer messageBox = ByteBuffer.allocate(clen);
messageBox.position(Curve.Size.BOXZERO.bytes());
msg.transfer(messageBox, 16, msg.size() - 16);
int rc = cryptoBox.openAfternm(messagePlaintext, messageBox, clen, messageNonce, cnPrecom);
if (rc == 0) {
Msg decoded = new Msg(clen - 1 - Curve.Size.ZERO.bytes());
byte flags = messagePlaintext.get(Curve.Size.ZERO.bytes());
if ((flags & 0x01) != 0) {
decoded.setFlags(Msg.MORE);
}
if ((flags & 0x02) != 0) {
decoded.setFlags(Msg.COMMAND);
}
messagePlaintext.position(Curve.Size.ZERO.bytes() + 1);
decoded.put(messagePlaintext);
return decoded;
}
else {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
errno.set(ZError.EPROTO);
return null;
}
}
@Override
public Status status()
{
if (state == State.CONNECTED) {
return Status.READY;
}
else if (state == State.ERROR_RECEIVED) {
return Status.ERROR;
}
else {
return Status.HANDSHAKING;
}
}
@Override
public int zapMsgAvailable()
{
return 0;
}
private int produceHello(Msg msg)
{
ByteBuffer helloNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes());
ByteBuffer helloPlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 64);
ByteBuffer helloBox = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 80);
// Prepare the full nonce
helloNonce.put("CurveZMQHELLO---".getBytes(ZMQ.CHARSET));
Wire.putUInt64(helloNonce, cnNonce);
// Create Box [64 * %x0](C'->S)
int rc = cryptoBox.box(helloBox, helloPlaintext, helloPlaintext.capacity(), helloNonce, serverKey, cnSecret);
if (rc != 0) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
return -1;
}
msg.putShortString("HELLO");
// CurveZMQ major and minor version numbers
msg.put(1);
msg.put(0);
// Anti-amplification padding
msg.put(new byte[72]);
// Client public connection key
msg.put(cnPublic);
// Short nonce, prefixed by "CurveZMQHELLO---"
msg.put(helloNonce, 16, 8);
// Signature, Box [64 * %x0](C'->S)
msg.put(helloBox, Curve.Size.BOXZERO.bytes(), 80);
assert (msg.size() == 200);
cnNonce++;
return 0;
}
private int processWelcome(Msg msg)
{
if (msg.size() != 168) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY);
return ZError.EPROTO;
}
ByteBuffer welcomeNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes());
ByteBuffer welcomePlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 128);
ByteBuffer welcomeBox = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 144);
// Open Box [S' + cookie](C'->S)
welcomeBox.position(Curve.Size.BOXZERO.bytes());
msg.transfer(welcomeBox, 24, 144);
welcomeNonce.put("WELCOME-".getBytes(ZMQ.CHARSET));
msg.transfer(welcomeNonce, 8, 16);
int rc = cryptoBox.open(welcomePlaintext, welcomeBox, welcomeBox.capacity(), welcomeNonce, serverKey, cnSecret);
if (rc != 0) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
return ZError.EPROTO;
}
welcomePlaintext.position(Curve.Size.ZERO.bytes());
welcomePlaintext.get(cnServer);
welcomePlaintext.get(cnCookie);
// Message independent precomputation
rc = cryptoBox.beforenm(cnPrecom, cnServer, cnSecret);
assert (rc == 0);
state = State.SEND_INITIATE;
return 0;
}
private int produceInitiate(Msg msg)
{
ByteBuffer vouchNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes());
ByteBuffer vouchPlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 64);
ByteBuffer vouchBox = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 80);
// Create vouch = Box [C',S](C->S')
vouchPlaintext.position(Curve.Size.ZERO.bytes());
vouchPlaintext.put(cnPublic);
vouchPlaintext.put(serverKey);
vouchNonce.put("VOUCH---".getBytes(ZMQ.CHARSET));
vouchNonce.put(cryptoBox.random(16));
int rc = cryptoBox.box(vouchBox, vouchPlaintext, vouchPlaintext.capacity(), vouchNonce, cnServer, secretKey);
if (rc == -1) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
return -1;
}
// Assume here that metadata is limited to 256 bytes
ByteBuffer initiateNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes());
ByteBuffer initiatePlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 128 + 256);
ByteBuffer initiateBox = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 144 + 256);
// Create Box [C + vouch + metadata](C'->S')
initiatePlaintext.position(Curve.Size.ZERO.bytes());
initiatePlaintext.put(publicKey);
vouchNonce.limit(16 + 8).position(8);
initiatePlaintext.put(vouchNonce);
vouchBox.limit(Curve.Size.BOXZERO.bytes() + 80).position(Curve.Size.BOXZERO.bytes());
initiatePlaintext.put(vouchBox);
// Metadata starts after vouch
// Add socket type property
String socketType = socketType();
addProperty(initiatePlaintext, SOCKET_TYPE, socketType);
// Add identity property
if (options.type == ZMQ.ZMQ_REQ || options.type == ZMQ.ZMQ_DEALER || options.type == ZMQ.ZMQ_ROUTER) {
addProperty(initiatePlaintext, IDENTITY, options.identity);
}
int mlen = initiatePlaintext.position();
initiateNonce.put("CurveZMQINITIATE".getBytes(ZMQ.CHARSET));
Wire.putUInt64(initiateNonce, cnNonce);
rc = cryptoBox.box(initiateBox, initiatePlaintext, mlen, initiateNonce, cnServer, cnSecret);
if (rc == -1) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
return -1;
}
msg.putShortString("INITIATE");
// Cookie provided by the server in the WELCOME command
msg.put(cnCookie);
// Short nonce, prefixed by "CurveZMQINITIATE"
msg.put(initiateNonce, 16, 8);
// Box [C + vouch + metadata](C'->S')
msg.put(initiateBox, Curve.Size.BOXZERO.bytes(), mlen - Curve.Size.BOXZERO.bytes());
assert (msg.size() == 113 + mlen - Curve.Size.BOXZERO.bytes());
cnNonce++;
return 0;
}
private int processReady(Msg msg)
{
if (msg.size() < 30) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY);
return ZError.EPROTO;
}
int clen = Curve.Size.BOXZERO.bytes() + msg.size() - 14;
ByteBuffer readyNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes());
ByteBuffer readyPlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 256);
ByteBuffer readyBox = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 16 + 256);
readyBox.position(Curve.Size.BOXZERO.bytes());
msg.transfer(readyBox, 14, clen - Curve.Size.BOXZERO.bytes());
readyNonce.put("CurveZMQREADY---".getBytes(ZMQ.CHARSET));
msg.transfer(readyNonce, 6, 8);
cnPeerNonce = msg.getLong(6);
int rc = cryptoBox.openAfternm(readyPlaintext, readyBox, clen, readyNonce, cnPrecom);
if (rc != 0) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
return ZError.EPROTO;
}
readyPlaintext.limit(clen);
rc = parseMetadata(readyPlaintext, Curve.Size.ZERO.bytes(), false);
if (rc == 0) {
state = State.CONNECTED;
}
return rc;
}
private int processError(Msg msg)
{
if (state != State.EXPECT_WELCOME && state != State.EXPECT_READY) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
return ZError.EPROTO;
}
state = State.ERROR_RECEIVED;
return parseErrorMessage(msg);
}
}
jeromq-0.6.0/src/main/java/zmq/io/mechanism/curve/CurveServerMechanism.java 0000664 0000000 0000000 00000044730 14557711263 0026742 0 ustar 00root root 0000000 0000000 package zmq.io.mechanism.curve;
import static zmq.io.Metadata.IDENTITY;
import static zmq.io.Metadata.SOCKET_TYPE;
import java.nio.ByteBuffer;
import zmq.Msg;
import zmq.Options;
import zmq.ZError;
import zmq.ZMQ;
import zmq.io.SessionBase;
import zmq.io.mechanism.Mechanism;
import zmq.io.mechanism.Mechanisms;
import zmq.io.net.Address;
import zmq.util.Errno;
import zmq.util.Wire;
public class CurveServerMechanism extends Mechanism
{
private enum State
{
EXPECT_HELLO,
SEND_WELCOME,
EXPECT_INITIATE,
EXPECT_ZAP_REPLY,
SEND_READY,
SEND_ERROR,
ERROR_SENT,
CONNECTED
}
private long cnNonce;
private long cnPeerNonce;
// Our secret key (s)
private final byte[] secretKey;
// Our short-term public key (S')
private final byte[] cnPublic;
// Our short-term secret key (s')
private final byte[] cnSecret;
// Client's short-term public key (C')
private final byte[] cnClient = new byte[Curve.Size.PUBLICKEY.bytes()];
// Key used to produce cookie
private byte[] cookieKey;
// Intermediary buffer used to speed up boxing and unboxing.
private final byte[] cnPrecom = new byte[Curve.Size.BEFORENM.bytes()];
private State state;
private final Curve cryptoBox;
private final Errno errno;
public CurveServerMechanism(SessionBase session, Address peerAddress, Options options)
{
super(session, peerAddress, options);
this.state = State.EXPECT_HELLO;
cnNonce = 1;
cnPeerNonce = 1;
secretKey = options.curveSecretKey;
assert (secretKey != null && secretKey.length == Curve.Size.SECRETKEY.bytes());
cryptoBox = new Curve();
// Generate short-term key pair
byte[][] keys = cryptoBox.keypair();
assert (keys != null && keys.length == 2);
cnPublic = keys[0];
assert (cnPublic != null && cnPublic.length == Curve.Size.PUBLICKEY.bytes());
cnSecret = keys[1];
assert (cnSecret != null && cnSecret.length == Curve.Size.SECRETKEY.bytes());
errno = options.errno;
}
@Override
public int nextHandshakeCommand(Msg msg)
{
int rc;
switch (state) {
case SEND_WELCOME:
rc = produceWelcome(msg);
if (rc == 0) {
state = State.EXPECT_INITIATE;
}
break;
case SEND_READY:
rc = produceReady(msg);
if (rc == 0) {
state = State.CONNECTED;
}
break;
case SEND_ERROR:
rc = produceError(msg);
if (rc == 0) {
state = State.ERROR_SENT;
}
break;
default:
rc = ZError.EAGAIN;
break;
}
return rc;
}
@Override
public int processHandshakeCommand(Msg msg)
{
int rc;
switch (state) {
case EXPECT_HELLO:
rc = processHello(msg);
break;
case EXPECT_INITIATE:
rc = processInitiate(msg);
break;
default:
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED);
rc = ZError.EPROTO;
break;
}
return rc;
}
@Override
public Msg encode(Msg msg)
{
assert (state == State.CONNECTED);
byte flags = 0;
if (msg.hasMore()) {
flags |= 0x01;
}
if (msg.isCommand()) {
flags |= 0x02;
}
ByteBuffer messageNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes());
messageNonce.put("CurveZMQMESSAGES".getBytes(ZMQ.CHARSET));
Wire.putUInt64(messageNonce, cnNonce);
int mlen = Curve.Size.ZERO.bytes() + 1 + msg.size();
ByteBuffer messagePlaintext = ByteBuffer.allocate(mlen);
messagePlaintext.put(Curve.Size.ZERO.bytes(), flags);
messagePlaintext.position(Curve.Size.ZERO.bytes() + 1);
msg.transfer(messagePlaintext, 0, msg.size());
ByteBuffer messageBox = ByteBuffer.allocate(mlen);
int rc = cryptoBox.afternm(messageBox, messagePlaintext, mlen, messageNonce, cnPrecom);
assert (rc == 0);
Msg encoded = new Msg(16 + mlen - Curve.Size.BOXZERO.bytes());
encoded.putShortString("MESSAGE");
encoded.put(messageNonce, 16, 8);
encoded.put(messageBox, Curve.Size.BOXZERO.bytes(), mlen - Curve.Size.BOXZERO.bytes());
cnNonce++;
return encoded;
}
@Override
public Msg decode(Msg msg)
{
assert (state == State.CONNECTED);
if (!compare(msg, "MESSAGE", true)) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
errno.set(ZError.EPROTO);
return null;
}
if (msg.size() < 33) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE);
errno.set(ZError.EPROTO);
return null;
}
ByteBuffer messageNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes());
messageNonce.put("CurveZMQMESSAGEC".getBytes(ZMQ.CHARSET));
msg.transfer(messageNonce, 8, 8);
long nonce = msg.getLong(8);
if (nonce <= cnPeerNonce) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_SEQUENCE);
errno.set(ZError.EPROTO);
return null;
}
cnPeerNonce = nonce;
int clen = Curve.Size.BOXZERO.bytes() + msg.size() - 16;
ByteBuffer messagePlaintext = ByteBuffer.allocate(clen);
ByteBuffer messageBox = ByteBuffer.allocate(clen);
messageBox.position(Curve.Size.BOXZERO.bytes());
msg.transfer(messageBox, 16, msg.size() - 16);
int rc = cryptoBox.openAfternm(messagePlaintext, messageBox, clen, messageNonce, cnPrecom);
if (rc == 0) {
Msg decoded = new Msg(clen - 1 - Curve.Size.ZERO.bytes());
byte flags = messagePlaintext.get(Curve.Size.ZERO.bytes());
if ((flags & 0x01) != 0) {
decoded.setFlags(Msg.MORE);
}
if ((flags & 0x02) != 0) {
decoded.setFlags(Msg.COMMAND);
}
messagePlaintext.position(Curve.Size.ZERO.bytes() + 1);
decoded.put(messagePlaintext);
return decoded;
}
else {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
errno.set(ZError.EPROTO);
return null;
}
}
@Override
public int zapMsgAvailable()
{
if (state != State.EXPECT_ZAP_REPLY) {
return ZError.EFSM;
}
int rc = receiveAndProcessZapReply();
if (rc == 0) {
state = "200".equals(statusCode) ? State.SEND_READY : State.SEND_ERROR;
}
return rc;
}
@Override
public Status status()
{
if (state == State.CONNECTED) {
return Status.READY;
}
else if (state == State.ERROR_SENT) {
return Status.ERROR;
}
else {
return Status.HANDSHAKING;
}
}
private int processHello(Msg msg)
{
if (!compare(msg, "HELLO", true)) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
return ZError.EPROTO;
}
if (msg.size() != 200) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO);
return ZError.EPROTO;
}
byte major = msg.get(6);
byte minor = msg.get(7);
if (major != 1 || minor != 0) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO);
return ZError.EPROTO;
}
// Save client's short-term public key (C')
msg.getBytes(80, cnClient, 0, Curve.Size.PUBLICKEY.bytes());
ByteBuffer helloNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes());
ByteBuffer helloPlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 64);
ByteBuffer helloBox = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 80);
helloNonce.put("CurveZMQHELLO---".getBytes(ZMQ.CHARSET));
msg.transfer(helloNonce, 112, 8);
cnPeerNonce = msg.getLong(112);
helloBox.position(Curve.Size.BOXZERO.bytes());
msg.transfer(helloBox, 120, 80);
// Open Box [64 * %x0](C'->S)
int rc = cryptoBox.open(helloPlaintext, helloBox, helloBox.capacity(), helloNonce, cnClient, secretKey);
if (rc != 0) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
state = State.SEND_ERROR;
statusCode = null;
return 0;
}
state = State.SEND_WELCOME;
return 0;
}
private int produceWelcome(Msg msg)
{
ByteBuffer cookieNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes());
ByteBuffer cookiePlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 64);
ByteBuffer cookieCiphertext = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 80);
// Create full nonce for encryption
// 8-byte prefix plus 16-byte random nonce
cookieNonce.put("COOKIE--".getBytes(ZMQ.CHARSET));
cookieNonce.put(cryptoBox.random(16));
// Generate cookie = Box [C' + s'](t)
cookiePlaintext.position(Curve.Size.ZERO.bytes());
cookiePlaintext.put(cnClient);
cookiePlaintext.put(cnSecret);
// Generate fresh cookie key
cookieKey = cryptoBox.random(Curve.Size.KEY.bytes());
// Encrypt using symmetric cookie key
int rc = cryptoBox
.secretbox(cookieCiphertext, cookiePlaintext, cookiePlaintext.capacity(), cookieNonce, cookieKey);
assert (rc == 0);
ByteBuffer welcomeNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes());
ByteBuffer welcomePlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 128);
ByteBuffer welcomeCiphertext = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 144);
// Create full nonce for encryption
// 8-byte prefix plus 16-byte random nonce
welcomeNonce.put("WELCOME-".getBytes(ZMQ.CHARSET));
welcomeNonce.put(cryptoBox.random(Curve.Size.NONCE.bytes() - 8));
// Create 144-byte Box [S' + cookie](S->C')
welcomePlaintext.position(Curve.Size.ZERO.bytes());
welcomePlaintext.put(cnPublic);
cookieNonce.limit(16 + 8).position(8);
welcomePlaintext.put(cookieNonce);
cookieCiphertext.limit(Curve.Size.BOXZERO.bytes() + 80).position(Curve.Size.BOXZERO.bytes());
welcomePlaintext.put(cookieCiphertext);
rc = cryptoBox.box(
welcomeCiphertext,
welcomePlaintext,
welcomePlaintext.capacity(),
welcomeNonce,
cnClient,
secretKey);
if (rc == -1) {
return -1;
}
msg.putShortString("WELCOME");
msg.put(welcomeNonce, 8, 16);
msg.put(welcomeCiphertext, Curve.Size.BOXZERO.bytes(), 144);
assert (msg.size() == 168);
return 0;
}
private int processInitiate(Msg msg)
{
if (!compare(msg, "INITIATE", true)) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
return ZError.EPROTO;
}
if (msg.size() < 257) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE);
return ZError.EPROTO;
}
ByteBuffer cookieNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes());
ByteBuffer cookiePlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 64);
ByteBuffer cookieBox = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 80);
// Open Box [C' + s'](t)
cookieBox.position(Curve.Size.BOXZERO.bytes());
msg.transfer(cookieBox, 25, 80);
cookieNonce.put("COOKIE--".getBytes(ZMQ.CHARSET));
msg.transfer(cookieNonce, 9, 16);
int rc = cryptoBox.secretboxOpen(cookiePlaintext, cookieBox, cookieBox.capacity(), cookieNonce, cookieKey);
if (rc != 0) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
return ZError.EPROTO;
}
// Check cookie plain text is as expected [C' + s']
if (!compare(cookiePlaintext, cnClient, Curve.Size.ZERO.bytes(), 32)
|| !compare(cookiePlaintext, cnSecret, Curve.Size.ZERO.bytes() + 32, 32)) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
return ZError.EPROTO;
}
int clen = msg.size() - 113 + Curve.Size.BOXZERO.bytes();
ByteBuffer initiateNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes());
ByteBuffer initiatePlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 128 + 256);
ByteBuffer initiateBox = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 144 + 256);
// Open Box [C + vouch + metadata](C'->S')
initiateBox.position(Curve.Size.BOXZERO.bytes());
msg.transfer(initiateBox, 113, clen - Curve.Size.BOXZERO.bytes());
initiateNonce.put("CurveZMQINITIATE".getBytes(ZMQ.CHARSET));
msg.transfer(initiateNonce, 105, 8);
cnPeerNonce = msg.getLong(105);
rc = cryptoBox.open(initiatePlaintext, initiateBox, clen, initiateNonce, cnClient, cnSecret);
if (rc != 0) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
return ZError.EPROTO;
}
byte[] clientKey = new byte[128 + 256];
initiatePlaintext.position(Curve.Size.ZERO.bytes());
initiatePlaintext.get(clientKey);
ByteBuffer vouchNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes());
ByteBuffer vouchPlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 64);
ByteBuffer vouchBox = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 80);
// Open Box Box [C',S](C->S') and check contents
vouchBox.position(Curve.Size.BOXZERO.bytes());
initiatePlaintext.limit(Curve.Size.ZERO.bytes() + 48 + 80).position(Curve.Size.ZERO.bytes() + 48);
vouchBox.put(initiatePlaintext);
vouchNonce.put("VOUCH---".getBytes(ZMQ.CHARSET));
initiatePlaintext.limit(Curve.Size.ZERO.bytes() + 32 + 16).position(Curve.Size.ZERO.bytes() + 32);
vouchNonce.put(initiatePlaintext);
rc = cryptoBox.open(vouchPlaintext, vouchBox, vouchBox.capacity(), vouchNonce, clientKey, cnSecret);
if (rc != 0) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC);
return ZError.EPROTO;
}
// What we decrypted must be the client's short-term public key
if (!compare(vouchPlaintext, cnClient, Curve.Size.ZERO.bytes(), 32)) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE);
return ZError.EPROTO;
}
// Precompute connection secret from client key
rc = cryptoBox.beforenm(cnPrecom, cnClient, cnSecret);
assert (rc == 0);
// Use ZAP protocol (RFC 27) to authenticate the user.
rc = session.zapConnect();
if (rc == 0) {
sendZapRequest(clientKey);
rc = receiveAndProcessZapReply();
if (rc == 0) {
state = "200".equals(statusCode) ? State.SEND_READY : State.SEND_ERROR;
}
else if (rc == ZError.EAGAIN) {
state = State.EXPECT_ZAP_REPLY;
}
else {
return -1;
}
}
else {
state = State.SEND_READY;
}
initiatePlaintext.position(0);
initiatePlaintext.limit(clen);
return parseMetadata(initiatePlaintext, Curve.Size.ZERO.bytes() + 128, false);
}
private int produceReady(Msg msg)
{
ByteBuffer readyNonce = ByteBuffer.allocate(Curve.Size.NONCE.bytes());
ByteBuffer readyPlaintext = ByteBuffer.allocate(Curve.Size.ZERO.bytes() + 256);
ByteBuffer readyBox = ByteBuffer.allocate(Curve.Size.BOXZERO.bytes() + 16 + 256);
// Create Box [metadata](S'->C')
readyPlaintext.position(Curve.Size.ZERO.bytes());
// Add socket type property
String socketType = socketType();
addProperty(readyPlaintext, SOCKET_TYPE, socketType);
// Add identity property
if (options.type == ZMQ.ZMQ_REQ || options.type == ZMQ.ZMQ_DEALER || options.type == ZMQ.ZMQ_ROUTER) {
addProperty(readyPlaintext, IDENTITY, options.identity);
}
int mlen = readyPlaintext.position();
readyNonce.put("CurveZMQREADY---".getBytes(ZMQ.CHARSET));
Wire.putUInt64(readyNonce, cnNonce);
int rc = cryptoBox.afternm(readyBox, readyPlaintext, mlen, readyNonce, cnPrecom);
assert (rc == 0);
msg.putShortString("READY");
// Short nonce, prefixed by "CurveZMQREADY---"
msg.put(readyNonce, 16, 8);
// Box [metadata](S'->C')
msg.put(readyBox, Curve.Size.BOXZERO.bytes(), mlen - Curve.Size.BOXZERO.bytes());
assert (msg.size() == 14 + mlen - Curve.Size.BOXZERO.bytes());
cnNonce++;
return 0;
}
private int produceError(Msg msg)
{
assert (statusCode == null || statusCode.length() == 3);
msg.putShortString("ERROR");
if (statusCode != null) {
msg.putShortString(statusCode);
}
else {
msg.putShortString("");
}
return 0;
}
private void sendZapRequest(byte[] key)
{
sendZapRequest(Mechanisms.CURVE, true);
// Credentials frame
Msg msg = new Msg(Curve.Size.PUBLICKEY.bytes());
msg.put(key, 0, Curve.Size.PUBLICKEY.bytes());
boolean rc = session.writeZapMsg(msg);
assert (rc);
}
}
jeromq-0.6.0/src/main/java/zmq/io/mechanism/gssapi/ 0000775 0000000 0000000 00000000000 14557711263 0022131 5 ustar 00root root 0000000 0000000 jeromq-0.6.0/src/main/java/zmq/io/mechanism/gssapi/GssapiClientMechanism.java 0000664 0000000 0000000 00000001400 14557711263 0027201 0 ustar 00root root 0000000 0000000 package zmq.io.mechanism.gssapi;
import zmq.Msg;
import zmq.Options;
import zmq.io.SessionBase;
import zmq.io.mechanism.Mechanism;
// TODO V4 implement GSSAPI
public class GssapiClientMechanism extends Mechanism
{
public GssapiClientMechanism(SessionBase session, Options options)
{
super(session, null, options);
throw new UnsupportedOperationException("GSSAPI mechanism is not yet implemented");
}
@Override
public Status status()
{
return null;
}
@Override
public int zapMsgAvailable()
{
return 0;
}
@Override
public int processHandshakeCommand(Msg msg)
{
return 0;
}
@Override
public int nextHandshakeCommand(Msg msg)
{
return 0;
}
}
jeromq-0.6.0/src/main/java/zmq/io/mechanism/gssapi/GssapiServerMechanism.java 0000664 0000000 0000000 00000001467 14557711263 0027246 0 ustar 00root root 0000000 0000000 package zmq.io.mechanism.gssapi;
import zmq.Msg;
import zmq.Options;
import zmq.io.SessionBase;
import zmq.io.mechanism.Mechanism;
import zmq.io.net.Address;
// TODO V4 implement GSSAPI
public class GssapiServerMechanism extends Mechanism
{
public GssapiServerMechanism(SessionBase session, Address peerAddress, Options options)
{
super(session, peerAddress, options);
throw new UnsupportedOperationException("GSSAPI mechanism is not yet implemented");
}
@Override
public Status status()
{
return null;
}
@Override
public int zapMsgAvailable()
{
return 0;
}
@Override
public int processHandshakeCommand(Msg msg)
{
return 0;
}
@Override
public int nextHandshakeCommand(Msg msg)
{
return 0;
}
}
jeromq-0.6.0/src/main/java/zmq/io/mechanism/plain/ 0000775 0000000 0000000 00000000000 14557711263 0021746 5 ustar 00root root 0000000 0000000 jeromq-0.6.0/src/main/java/zmq/io/mechanism/plain/PlainClientMechanism.java 0000664 0000000 0000000 00000010050 14557711263 0026634 0 ustar 00root root 0000000 0000000 package zmq.io.mechanism.plain;
import static zmq.io.Metadata.IDENTITY;
import static zmq.io.Metadata.SOCKET_TYPE;
import zmq.Msg;
import zmq.Options;
import zmq.ZError;
import zmq.ZMQ;
import zmq.io.SessionBase;
import zmq.io.mechanism.Mechanism;
public class PlainClientMechanism extends Mechanism
{
private enum State
{
SENDING_HELLO,
WAITING_FOR_WELCOME,
SENDING_INITIATE,
WAITING_FOR_READY,
ERROR_COMMAND_RECEIVED,
READY
}
private State state;
public PlainClientMechanism(SessionBase session, Options options)
{
super(session, null, options);
this.state = State.SENDING_HELLO;
}
@Override
public int nextHandshakeCommand(Msg msg)
{
int rc;
switch (state) {
case SENDING_HELLO:
rc = produceHello(msg);
if (rc == 0) {
state = State.WAITING_FOR_WELCOME;
}
break;
case SENDING_INITIATE:
rc = produceInitiate(msg);
if (rc == 0) {
state = State.WAITING_FOR_READY;
}
break;
default:
rc = ZError.EAGAIN;
break;
}
return rc;
}
@Override
public int processHandshakeCommand(Msg msg)
{
int rc;
int dataSize = msg.size();
if (dataSize >= 8 && compare(msg, "WELCOME", true)) {
rc = processWelcome(msg);
}
else if (dataSize >= 6 && compare(msg, "READY", true)) {
rc = processReady(msg);
}
else if (dataSize >= 6 && compare(msg, "ERROR", true)) {
rc = processError(msg);
}
else {
// Temporary support for security debugging
System.out.println("PLAIN Client I: invalid handshake command");
rc = ZError.EPROTO;
}
return rc;
}
@Override
public Status status()
{
if (state == State.READY) {
return Status.READY;
}
else if (state == State.ERROR_COMMAND_RECEIVED) {
return Status.ERROR;
}
else {
return Status.HANDSHAKING;
}
}
@Override
public int zapMsgAvailable()
{
return 0;
}
private int produceHello(Msg msg)
{
String plainUsername = options.plainUsername;
assert (plainUsername.length() < 256);
String plainPassword = options.plainPassword;
assert (plainPassword.length() < 256);
msg.putShortString("HELLO");
msg.putShortString(plainUsername);
msg.putShortString(plainPassword);
return 0;
}
private int processWelcome(Msg msg)
{
if (state != State.WAITING_FOR_WELCOME) {
return ZError.EPROTO;
}
if (msg.size() != 8) {
return ZError.EPROTO;
}
state = State.SENDING_INITIATE;
return 0;
}
private int produceInitiate(Msg msg)
{
// Add mechanism string
msg.putShortString("INITIATE");
// Add socket type property
String socketType = socketType();
addProperty(msg, SOCKET_TYPE, socketType);
// Add identity property
if (options.type == ZMQ.ZMQ_REQ || options.type == ZMQ.ZMQ_DEALER || options.type == ZMQ.ZMQ_ROUTER) {
addProperty(msg, IDENTITY, options.identity);
}
return 0;
}
private int processReady(Msg msg)
{
if (state != State.WAITING_FOR_READY) {
return ZError.EPROTO;
}
int rc = parseMetadata(msg, 6, false);
if (rc == 0) {
state = State.READY;
}
return rc;
}
private int processError(Msg msg)
{
if (state != State.WAITING_FOR_WELCOME && state != State.WAITING_FOR_READY) {
session.getSocket().eventHandshakeFailedProtocol(session.getEndpoint(), ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND);
return ZError.EPROTO;
}
state = State.ERROR_COMMAND_RECEIVED;
return parseErrorMessage(msg);
}
}
jeromq-0.6.0/src/main/java/zmq/io/mechanism/plain/PlainServerMechanism.java 0000664 0000000 0000000 00000013414 14557711263 0026673 0 ustar 00root root 0000000 0000000 package zmq.io.mechanism.plain;
import static zmq.io.Metadata.IDENTITY;
import static zmq.io.Metadata.SOCKET_TYPE;
import zmq.Msg;
import zmq.Options;
import zmq.ZError;
import zmq.ZMQ;
import zmq.io.SessionBase;
import zmq.io.mechanism.Mechanism;
import zmq.io.mechanism.Mechanisms;
import zmq.io.net.Address;
public class PlainServerMechanism extends Mechanism
{
private enum State
{
WAITING_FOR_HELLO,
SENDING_WELCOME,
WAITING_FOR_INITIATE,
SENDING_READY,
WAITING_FOR_ZAP_REPLY,
SENDING_ERROR,
ERROR_COMMAND_SENT,
READY
}
private State state;
public PlainServerMechanism(SessionBase session, Address peerAddress, Options options)
{
super(session, peerAddress, options);
this.state = State.WAITING_FOR_HELLO;
}
@Override
public int nextHandshakeCommand(Msg msg)
{
int rc;
switch (state) {
case SENDING_WELCOME:
rc = produceWelcome(msg);
if (rc == 0) {
state = State.WAITING_FOR_INITIATE;
}
break;
case SENDING_READY:
rc = produceReady(msg);
if (rc == 0) {
state = State.READY;
}
break;
case SENDING_ERROR:
rc = produceError(msg);
if (rc == 0) {
state = State.ERROR_COMMAND_SENT;
}
break;
default:
rc = ZError.EAGAIN;
break;
}
return rc;
}
@Override
public int processHandshakeCommand(Msg msg)
{
int rc;
switch (state) {
case WAITING_FOR_HELLO:
rc = produceHello(msg);
break;
case WAITING_FOR_INITIATE:
rc = produceInitiate(msg);
break;
default:
rc = ZError.EPROTO;
break;
}
return rc;
}
@Override
public Status status()
{
if (state == State.READY) {
return Status.READY;
}
else if (state == State.ERROR_COMMAND_SENT) {
return Status.ERROR;
}
else {
return Status.HANDSHAKING;
}
}
@Override
public int zapMsgAvailable()
{
if (state != State.WAITING_FOR_ZAP_REPLY) {
return ZError.EFSM;
}
int rc = receiveAndProcessZapReply();
if (rc == 0) {
state = "200".equals(statusCode) ? State.SENDING_WELCOME : State.SENDING_ERROR;
}
return rc;
}
private int produceHello(Msg msg)
{
int bytesLeft = msg.size();
int index = 0;
if (bytesLeft < 6 || !compare(msg, "HELLO", true)) {
return ZError.EPROTO;
}
bytesLeft -= 6;
index += 6;
if (bytesLeft < 1) {
return ZError.EPROTO;
}
byte length = msg.get(index);
bytesLeft -= 1;
if (bytesLeft < length) {
return ZError.EPROTO;
}
byte[] tmp = new byte[length];
index += 1;
msg.getBytes(index, tmp, 0, length);
byte[] username = tmp;
bytesLeft -= length;
index += length;
length = msg.get(index);
bytesLeft -= 1;
if (bytesLeft < length) {
return ZError.EPROTO;
}
tmp = new byte[length];
index += 1;
msg.getBytes(index, tmp, 0, length);
byte[] password = tmp;
bytesLeft -= length;
// index += length;
if (bytesLeft > 0) {
return ZError.EPROTO;
}
// Use ZAP protocol (RFC 27) to authenticate the user.
int rc = session.zapConnect();
if (rc == 0) {
sendZapRequest(username, password);
rc = receiveAndProcessZapReply();
if (rc == 0) {
state = "200".equals(statusCode) ? State.SENDING_WELCOME : State.SENDING_ERROR;
}
else if (rc == ZError.EAGAIN) {
state = State.WAITING_FOR_ZAP_REPLY;
}
else {
return -1;
}
}
else {
state = State.SENDING_WELCOME;
}
return 0;
}
private int produceWelcome(Msg msg)
{
msg.putShortString("WELCOME");
return 0;
}
private int produceInitiate(Msg msg)
{
int bytesLeft = msg.size();
if (bytesLeft < 9 || !compare(msg, "INITIATE", true)) {
return ZError.EPROTO;
}
int rc = parseMetadata(msg, 9, false);
if (rc == 0) {
state = State.SENDING_READY;
}
return rc;
}
private int produceReady(Msg msg)
{
// Add command name
msg.putShortString("READY");
// Add socket type property
String socketType = socketType();
addProperty(msg, SOCKET_TYPE, socketType);
// Add identity property
if (options.type == ZMQ.ZMQ_REQ || options.type == ZMQ.ZMQ_DEALER || options.type == ZMQ.ZMQ_ROUTER) {
addProperty(msg, IDENTITY, options.identity);
}
return 0;
}
private int produceError(Msg msg)
{
assert (statusCode != null && statusCode.length() == 3);
msg.putShortString("ERROR");
msg.putShortString(statusCode);
return 0;
}
private void sendZapRequest(byte[] username, byte[] password)
{
sendZapRequest(Mechanisms.PLAIN, true);
// Username frame
Msg msg = new Msg(username.length);
msg.setFlags(Msg.MORE);
msg.put(username);
boolean rc = session.writeZapMsg(msg);
assert (rc);
// Password frame
msg = new Msg(password.length);
msg.put(password);
rc = session.writeZapMsg(msg);
assert (rc);
}
}
jeromq-0.6.0/src/main/java/zmq/io/net/ 0000775 0000000 0000000 00000000000 14557711263 0017465 5 ustar 00root root 0000000 0000000 jeromq-0.6.0/src/main/java/zmq/io/net/Address.java 0000664 0000000 0000000 00000005130 14557711263 0021714 0 ustar 00root root 0000000 0000000 package zmq.io.net;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
public class Address
{
public interface IZAddress
{
ProtocolFamily family();
String toString(int port);
InetSocketAddress resolve(String name, boolean ipv6, boolean local);
SocketAddress address();
SocketAddress sourceAddress();
}
private final NetProtocol protocol;
private final String address;
private IZAddress resolved;
/**
* @param protocol
* @param address
* @throws IllegalArgumentException if the protocol name can be matched to an actual supported protocol
*/
@Deprecated
public Address(final String protocol, final String address)
{
this.protocol = NetProtocol.getProtocol(protocol);
this.address = address;
resolved = null;
}
/**
* @param protocol
* @param address
*/
public Address(final NetProtocol protocol, final String address)
{
this.protocol = protocol;
this.address = address;
resolved = null;
}
/**
* @param socketAddress
* @throws IllegalArgumentException if the SocketChannel is not an IP socket address
*/
public Address(SocketAddress socketAddress)
{
if (socketAddress instanceof InetSocketAddress) {
InetSocketAddress sockAddr = (InetSocketAddress) socketAddress;
this.address = sockAddr.getAddress().getHostAddress() + ":" + sockAddr.getPort();
protocol = NetProtocol.tcp;
resolved = null;
}
else {
throw new IllegalArgumentException("Not a IP socket address");
}
}
@Override
public String toString()
{
if (isResolved()) {
return resolved.toString();
}
else if (protocol != null && !address.isEmpty()) {
return protocol.name() + "://" + address;
}
else {
return "";
}
}
public NetProtocol protocol()
{
return protocol;
}
public String address()
{
return address;
}
public String host()
{
final int portDelimiter = address.lastIndexOf(':');
if (portDelimiter > 0) {
return address.substring(0, portDelimiter);
}
return address;
}
public IZAddress resolved()
{
return resolved;
}
public boolean isResolved()
{
return resolved != null;
}
public IZAddress resolve(boolean ipv6)
{
resolved = protocol.zresolve(address, ipv6);
return resolved;
}
}
jeromq-0.6.0/src/main/java/zmq/io/net/Listener.java 0000664 0000000 0000000 00000001034 14557711263 0022113 0 ustar 00root root 0000000 0000000 package zmq.io.net;
import zmq.Options;
import zmq.Own;
import zmq.SocketBase;
import zmq.io.IOThread;
import zmq.poll.IPollEvents;
public abstract class Listener extends Own implements IPollEvents
{
// Socket the listener belongs to.
protected final SocketBase socket;
protected Listener(IOThread ioThread, SocketBase socket, final Options options)
{
super(ioThread, options);
this.socket = socket;
}
public abstract boolean setAddress(String addr);
public abstract String getAddress();
}
jeromq-0.6.0/src/main/java/zmq/io/net/NetProtocol.java 0000664 0000000 0000000 00000007253 14557711263 0022607 0 ustar 00root root 0000000 0000000 package zmq.io.net;
import java.util.Arrays;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Collectors;
import zmq.Options;
import zmq.SocketBase;
import zmq.io.IOThread;
import zmq.io.net.Address.IZAddress;
import zmq.io.net.ipc.IpcAddress;
import zmq.io.net.ipc.IpcListener;
import zmq.io.net.tcp.TcpAddress;
import zmq.io.net.tcp.TcpListener;
import zmq.io.net.tipc.TipcListener;
import zmq.socket.Sockets;
public enum NetProtocol
{
inproc(true, false, false),
ipc(true, false, false)
{
@Override
public Listener getListener(IOThread ioThread, SocketBase socket,
Options options)
{
return new IpcListener(ioThread, socket, options);
}
@Override
public void resolve(Address paddr, boolean ipv6)
{
paddr.resolve(ipv6);
}
@Override
public IZAddress zresolve(String addr, boolean ipv6)
{
return new IpcAddress(addr);
}
},
tcp(true, false, false)
{
@Override
public Listener getListener(IOThread ioThread, SocketBase socket,
Options options)
{
return new TcpListener(ioThread, socket, options);
}
@Override
public void resolve(Address paddr, boolean ipv6)
{
paddr.resolve(ipv6);
}
@Override
public IZAddress zresolve(String addr, boolean ipv6)
{
return new TcpAddress(addr, ipv6);
}
},
// PGM does not support subscription forwarding; ask for all data to be
// sent to this pipe. (same for NORM, currently?)
pgm(false, true, true, Sockets.PUB, Sockets.SUB, Sockets.XPUB, Sockets.XPUB),
epgm(false, true, true, Sockets.PUB, Sockets.SUB, Sockets.XPUB, Sockets.XPUB),
tipc(false, false, false)
{
@Override
public Listener getListener(IOThread ioThread, SocketBase socket,
Options options)
{
return new TipcListener(ioThread, socket, options);
}
@Override
public void resolve(Address paddr, boolean ipv6)
{
paddr.resolve(ipv6);
}
},
norm(false, true, true);
public final boolean valid;
public final boolean subscribe2all;
public final boolean isMulticast;
private final Set compatibles;
NetProtocol(boolean implemented, boolean subscribe2all, boolean isMulticast, Sockets... compatibles)
{
valid = implemented;
this.compatibles = Arrays.stream(compatibles).map(Sockets::ordinal).collect(Collectors.toSet());
this.subscribe2all = subscribe2all;
this.isMulticast = isMulticast;
}
/**
* @param protocol name
* @throws IllegalArgumentException if the protocol name can be matched to an actual supported protocol
* @return
*/
public static NetProtocol getProtocol(String protocol)
{
try {
return valueOf(protocol.toLowerCase(Locale.ENGLISH));
}
catch (NullPointerException | IllegalArgumentException e) {
throw new IllegalArgumentException("Unknown protocol: \"" + protocol + "\"");
}
}
public final boolean compatible(int type)
{
return compatibles.isEmpty() || compatibles.contains(type);
}
public Listener getListener(IOThread ioThread, SocketBase socket, Options options)
{
return null;
}
public void resolve(Address paddr, boolean ipv6)
{
// TODO V4 init address for pgm & epgm
}
public IZAddress zresolve(String addr, boolean ipv6)
{
return null;
}
}
jeromq-0.6.0/src/main/java/zmq/io/net/ProtocolFamily.java 0000664 0000000 0000000 00000000251 14557711263 0023271 0 ustar 00root root 0000000 0000000 package zmq.io.net;
/**
* Replacement of ProtocolFamily from SDK so it can be used in Android environments.
*/
public interface ProtocolFamily
{
String name();
}
jeromq-0.6.0/src/main/java/zmq/io/net/SelectorProviderChooser.java 0000664 0000000 0000000 00000000702 14557711263 0025145 0 ustar 00root root 0000000 0000000 package zmq.io.net;
import java.nio.channels.spi.SelectorProvider;
import zmq.Options;
/**
* By implementing this class, it's possible to change the kind of channel used in tcp connections.
* It allows to easily wrap ZMQ socket in custom socket for TLS protection or other kind of trick.
*
* @author Fabrice Bacchella
*
*/
public interface SelectorProviderChooser
{
SelectorProvider choose(Address.IZAddress addr, Options options);
}
jeromq-0.6.0/src/main/java/zmq/io/net/StandardProtocolFamily.java 0000664 0000000 0000000 00000000317 14557711263 0024755 0 ustar 00root root 0000000 0000000 package zmq.io.net;
/**
* Replacement of StandardProtocolFamily from SDK so it can be used in Android environments.
*/
public enum StandardProtocolFamily implements ProtocolFamily
{
INET,
INET6
}
jeromq-0.6.0/src/main/java/zmq/io/net/ipc/ 0000775 0000000 0000000 00000000000 14557711263 0020240 5 ustar 00root root 0000000 0000000 jeromq-0.6.0/src/main/java/zmq/io/net/ipc/IpcAddress.java 0000664 0000000 0000000 00000006626 14557711263 0023136 0 ustar 00root root 0000000 0000000 package zmq.io.net.ipc;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketAddress;
import java.net.SocketException;
import java.util.Enumeration;
import zmq.ZMQ;
import zmq.io.net.Address;
import zmq.io.net.ProtocolFamily;
import zmq.io.net.StandardProtocolFamily;
import zmq.io.net.tcp.TcpAddress;
import zmq.util.Utils;
public class IpcAddress implements Address.IZAddress
{
public static class IpcAddressMask extends TcpAddress
{
public IpcAddressMask(String addr, boolean ipv6)
{
super(addr, ipv6);
}
public boolean matchAddress(SocketAddress addr)
{
return address().equals(addr);
}
}
private String name;
private final InetSocketAddress address;
private final SocketAddress sourceAddress;
public IpcAddress(String addr)
{
String[] strings = addr.split(";");
address = resolve(strings[0], ZMQ.PREFER_IPV6, true);
if (strings.length == 2 && !"".equals(strings[1])) {
sourceAddress = resolve(strings[1], ZMQ.PREFER_IPV6, true);
}
else {
sourceAddress = null;
}
}
@Override
public String toString()
{
if (name == null) {
return "";
}
return "ipc://" + name;
}
@Override
public String toString(int port)
{
if ("*".equals(name)) {
String suffix = Utils.unhash(port - 10000);
return "ipc://" + suffix;
}
return toString();
}
@Override
public InetSocketAddress resolve(String name, boolean ipv6, boolean loopback)
{
this.name = name;
int hash = name.hashCode();
if ("*".equals(name)) {
hash = 0;
}
else {
if (hash < 0) {
hash = -hash;
}
hash = hash % 55536;
hash += 10000;
}
return new InetSocketAddress(findAddress(ipv6, loopback), hash);
}
@Override
public SocketAddress address()
{
return address;
}
@Override
public ProtocolFamily family()
{
return StandardProtocolFamily.INET;
}
@Override
public SocketAddress sourceAddress()
{
return sourceAddress;
}
private InetAddress findAddress(boolean ipv6, boolean loopback)
{
Class extends InetAddress> addressClass = ipv6 ? Inet6Address.class : Inet4Address.class;
try {
for (Enumeration interfaces = NetworkInterface
.getNetworkInterfaces(); interfaces.hasMoreElements(); ) {
NetworkInterface net = interfaces.nextElement();
for (Enumeration addresses = net.getInetAddresses(); addresses.hasMoreElements(); ) {
InetAddress inetAddress = addresses.nextElement();
if (inetAddress.isLoopbackAddress() == loopback && addressClass.isInstance(inetAddress)) {
return inetAddress;
}
}
}
}
catch (SocketException e) {
throw new IllegalArgumentException(e);
}
throw new IllegalArgumentException("no address found " + (ipv6 ? "IPV6" : "IPV4") + (loopback ? "local" : ""));
}
}
jeromq-0.6.0/src/main/java/zmq/io/net/ipc/IpcConnecter.java 0000664 0000000 0000000 00000000617 14557711263 0023463 0 ustar 00root root 0000000 0000000 package zmq.io.net.ipc;
import zmq.Options;
import zmq.io.IOThread;
import zmq.io.SessionBase;
import zmq.io.net.Address;
import zmq.io.net.tcp.TcpConnecter;
public class IpcConnecter extends TcpConnecter
{
public IpcConnecter(IOThread ioThread, SessionBase session, final Options options, final Address addr, boolean wait)
{
super(ioThread, session, options, addr, wait);
}
}
jeromq-0.6.0/src/main/java/zmq/io/net/ipc/IpcListener.java 0000664 0000000 0000000 00000001642 14557711263 0023327 0 ustar 00root root 0000000 0000000 package zmq.io.net.ipc;
import java.net.InetSocketAddress;
import zmq.Options;
import zmq.SocketBase;
import zmq.io.IOThread;
import zmq.io.net.tcp.TcpListener;
// fake Unix domain socket
public class IpcListener extends TcpListener
{
private IpcAddress address;
public IpcListener(IOThread ioThread, SocketBase socket, final Options options)
{
super(ioThread, socket, options);
}
// Get the bound address for use with wildcards
@Override
public String getAddress()
{
if (((InetSocketAddress) address.address()).getPort() == 0) {
return address(address);
}
return address.toString();
}
// Set address to listen on.
@Override
public boolean setAddress(String addr)
{
address = new IpcAddress(addr);
InetSocketAddress sock = (InetSocketAddress) address.address();
return super.setAddress(sock);
}
}
jeromq-0.6.0/src/main/java/zmq/io/net/norm/ 0000775 0000000 0000000 00000000000 14557711263 0020440 5 ustar 00root root 0000000 0000000 jeromq-0.6.0/src/main/java/zmq/io/net/norm/NormEngine.java 0000664 0000000 0000000 00000000670 14557711263 0023347 0 ustar 00root root 0000000 0000000 package zmq.io.net.norm;
import zmq.Options;
import zmq.io.EngineNotImplemented;
import zmq.io.IOThread;
import zmq.io.net.Address;
// TODO V4 implement NORM engine
public class NormEngine extends EngineNotImplemented
{
public NormEngine(IOThread ioThread, Options options)
{
throw new UnsupportedOperationException();
}
public boolean init(Address addr, boolean b, boolean c)
{
return false;
}
}
jeromq-0.6.0/src/main/java/zmq/io/net/pgm/ 0000775 0000000 0000000 00000000000 14557711263 0020250 5 ustar 00root root 0000000 0000000 jeromq-0.6.0/src/main/java/zmq/io/net/pgm/PgmReceiver.java 0000664 0000000 0000000 00000000675 14557711263 0023333 0 ustar 00root root 0000000 0000000 package zmq.io.net.pgm;
import zmq.Options;
import zmq.io.EngineNotImplemented;
import zmq.io.IOThread;
import zmq.io.net.Address;
//TODO V4 implement pgm receiver
public class PgmReceiver extends EngineNotImplemented
{
public PgmReceiver(IOThread ioThread, Options options)
{
throw new UnsupportedOperationException();
}
public boolean init(boolean udpEncapsulation, Address addr)
{
return false;
}
}
jeromq-0.6.0/src/main/java/zmq/io/net/pgm/PgmSender.java 0000664 0000000 0000000 00000000670 14557711263 0023002 0 ustar 00root root 0000000 0000000 package zmq.io.net.pgm;
import zmq.Options;
import zmq.io.EngineNotImplemented;
import zmq.io.IOThread;
import zmq.io.net.Address;
// TODO V4 implement pgm sender
public class PgmSender extends EngineNotImplemented
{
public PgmSender(IOThread ioThread, Options options)
{
throw new UnsupportedOperationException();
}
public boolean init(boolean udpEncapsulation, Address addr)
{
return false;
}
}
jeromq-0.6.0/src/main/java/zmq/io/net/tcp/ 0000775 0000000 0000000 00000000000 14557711263 0020253 5 ustar 00root root 0000000 0000000 jeromq-0.6.0/src/main/java/zmq/io/net/tcp/SocksConnecter.java 0000664 0000000 0000000 00000006125 14557711263 0024045 0 ustar 00root root 0000000 0000000 package zmq.io.net.tcp;
import zmq.Options;
import zmq.io.IOThread;
import zmq.io.SessionBase;
import zmq.io.net.Address;
import zmq.io.net.NetProtocol;
// TODO continue socks connecter
public class SocksConnecter extends TcpConnecter
{
private enum Status
{
UNPLUGGED,
WAITING_FOR_RECONNECT_TIME,
WAITING_FOR_PROXY_CONNECTION,
SENDING_GREETING,
WAITING_FOR_CHOICE,
SENDING_REQUEST,
WAITING_FOR_RESPONSE
}
Status status;
// String representation of endpoint to connect to
String endpoint;
public SocksConnecter(IOThread ioThread, SessionBase session, final Options options, final Address addr,
final Address proxyAddr, boolean delayedStart)
{
super(ioThread, session, options, addr, delayedStart);
assert (NetProtocol.tcp.equals(addr.protocol()));
endpoint = proxyAddr.toString();
this.status = Status.UNPLUGGED;
throw new UnsupportedOperationException("Socks connecter is not implemented");
}
@Override
protected void processPlug()
{
if (delayedStart) {
startTimer();
}
else {
initiateConnect();
}
}
@Override
protected void processTerm(int linger)
{
switch (status) {
case UNPLUGGED:
break;
case WAITING_FOR_RECONNECT_TIME:
ioObject.cancelTimer(RECONNECT_TIMER_ID);
break;
case WAITING_FOR_PROXY_CONNECTION:
case SENDING_GREETING:
case WAITING_FOR_CHOICE:
case SENDING_REQUEST:
case WAITING_FOR_RESPONSE:
close();
break;
default:
break;
}
super.processTerm(linger);
}
@Override
public void inEvent()
{
assert (status != Status.UNPLUGGED && status != Status.WAITING_FOR_RECONNECT_TIME);
super.inEvent();
}
@Override
public void outEvent()
{
super.outEvent();
}
@Override
public void timerEvent(int id)
{
super.timerEvent(id);
}
// Internal function to start the actual connection establishment.
void initiateConnect()
{
}
int processServerResponse()
{
return -1;
}
void parseAddress(String address, String hostname, int port)
{
}
void connectToProxy()
{
}
void error()
{
}
// Internal function to start reconnect timer
void startTimer()
{
}
// Internal function to return a reconnect backoff delay.
// Will modify the current_reconnect_ivl used for next call
// Returns the currently used interval
int getNewReconnectIvl()
{
return -1;
}
// Open TCP connecting socket. Returns -1 in case of error,
// 0 if connect was successfull immediately. Returns -1 with
// EAGAIN errno if async connect was launched.
int open()
{
return -1;
}
// Get the file descriptor of newly created connection. Returns
// retired_fd if the connection was unsuccessfull.
void checkProxyConnection()
{
}
}
jeromq-0.6.0/src/main/java/zmq/io/net/tcp/TcpAddress.java 0000664 0000000 0000000 00000012076 14557711263 0023160 0 ustar 00root root 0000000 0000000 package zmq.io.net.tcp;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.UnknownHostException;
import org.zeromq.ZMQException;
import zmq.ZError;
import zmq.io.net.Address;
import zmq.io.net.ProtocolFamily;
import zmq.io.net.StandardProtocolFamily;
public class TcpAddress implements Address.IZAddress
{
public static class TcpAddressMask extends TcpAddress
{
public TcpAddressMask(String addr, boolean ipv6)
{
super(addr, ipv6);
}
public boolean matchAddress(SocketAddress addr)
{
return address().equals(addr);
}
}
private final InetSocketAddress address;
private final SocketAddress sourceAddress;
public TcpAddress(String addr, boolean ipv6)
{
String[] strings = addr.split(";");
address = resolve(strings[0], ipv6, false);
if (strings.length == 2 && !"".equals(strings[1])) {
sourceAddress = resolve(strings[1], ipv6, false);
}
else {
sourceAddress = null;
}
}
protected TcpAddress(InetSocketAddress address)
{
this.address = address;
sourceAddress = null;
}
@Override
public ProtocolFamily family()
{
if (address.getAddress() instanceof Inet6Address) {
return StandardProtocolFamily.INET6;
}
return StandardProtocolFamily.INET;
}
// The opposite to resolve()
@Override
public String toString()
{
return toString(address.getPort());
}
// The opposite to resolve()
@Override
public String toString(int port)
{
if (address == null) {
return "";
}
int addressPort = address.getPort();
if (addressPort == 0) {
addressPort = port;
}
if (address.getAddress() instanceof Inet6Address) {
return "tcp://[" + address.getAddress().getHostAddress() + "]:" + addressPort;
}
else {
return "tcp://" + address.getAddress().getHostAddress() + ":" + addressPort;
}
}
/**
* @param name
* @param ipv6
* @param local ignored
* @return the resolved address
* @see zmq.io.net.Address.IZAddress#resolve(java.lang.String, boolean, boolean)
*/
@Override
public InetSocketAddress resolve(String name, boolean ipv6, boolean local)
{
// Find the ':' at end that separates address from the port number.
int delimiter = name.lastIndexOf(':');
if (delimiter < 0) {
throw new IllegalArgumentException(name);
}
// Separate the address/port.
String addrStr = name.substring(0, delimiter);
String portStr = name.substring(delimiter + 1);
// Remove square brackets around the address, if any.
if (addrStr.length() >= 2 && addrStr.charAt(0) == '[' && addrStr.charAt(addrStr.length() - 1) == ']') {
addrStr = addrStr.substring(1, addrStr.length() - 1);
}
int port;
// Allow 0 specifically, to detect invalid port error in atoi if not
if (portStr.equals("*") || portStr.equals("0")) {
// Resolve wildcard to 0 to allow autoselection of port
port = 0;
}
else {
// Parse the port number (0 is not a valid port).
port = Integer.parseInt(portStr);
if (port == 0) {
throw new IllegalArgumentException(name);
}
}
InetAddress addrNet = null;
// '*' as unspecified address is not accepted in Java
// '::' for IPv6 is accepted
if (addrStr.equals("*")) {
addrStr = ipv6 ? "::" : "0.0.0.0";
}
try {
InetAddress[] addresses = InetAddress.getAllByName(addrStr);
if (ipv6) {
// prefer IPv6: return the first ipv6 or the first value if not found
for (InetAddress addr : addresses) {
if (addr instanceof Inet6Address) {
addrNet = addr;
break;
}
}
if (addrNet == null) {
addrNet = addresses[0];
}
}
else {
for (InetAddress addr : addresses) {
if (addr instanceof Inet4Address) {
addrNet = addr;
break;
}
}
}
}
catch (UnknownHostException e) {
throw new ZMQException(e.getMessage(), ZError.EADDRNOTAVAIL, e);
}
if (addrNet == null) {
throw new ZMQException(addrStr + " not found matching IPv4/IPv6 settings", ZError.EADDRNOTAVAIL);
}
return new InetSocketAddress(addrNet, port);
}
@Override
public SocketAddress address()
{
return address;
}
@Override
public SocketAddress sourceAddress()
{
return sourceAddress;
}
}
jeromq-0.6.0/src/main/java/zmq/io/net/tcp/TcpConnecter.java 0000664 0000000 0000000 00000024671 14557711263 0023517 0 ustar 00root root 0000000 0000000 package zmq.io.net.tcp;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.channels.SocketChannel;
import zmq.Options;
import zmq.Own;
import zmq.SocketBase;
import zmq.ZError;
import zmq.io.IOObject;
import zmq.io.IOThread;
import zmq.io.SessionBase;
import zmq.io.StreamEngine;
import zmq.io.net.Address;
import zmq.io.net.StandardProtocolFamily;
import zmq.poll.IPollEvents;
import zmq.poll.Poller;
import zmq.util.Utils;
// If 'delay' is true connecter first waits for a while, then starts
// connection process.
public class TcpConnecter extends Own implements IPollEvents
{
// ID of the timer used to delay the reconnection.
protected static final int RECONNECT_TIMER_ID = 1;
protected final IOObject ioObject;
// Address to connect to. Owned by session_base_t.
private final Address addr;
// Underlying socket.
private SocketChannel fd;
private Poller.Handle handle;
// If true, connecter is waiting a while before trying to connect.
protected final boolean delayedStart;
// True if a timer has been started.
private boolean timerStarted;
// Reference to the session we belong to.
private final SessionBase session;
// Current reconnect ivl, updated for backoff strategy
private int currentReconnectIvl;
// Socket
private final SocketBase socket;
public TcpConnecter(IOThread ioThread, SessionBase session, final Options options, final Address addr,
boolean delayedStart)
{
super(ioThread, options);
ioObject = new IOObject(ioThread, this);
this.addr = addr;
fd = null;
this.delayedStart = delayedStart;
timerStarted = false;
this.session = session;
currentReconnectIvl = this.options.reconnectIvl;
assert (this.addr != null);
// assert (NetProtocol.tcp.equals(this.addr.protocol())); // not always true, as ipc is emulated by tcp
socket = session.getSocket();
}
@Override
protected void destroy()
{
assert (!timerStarted);
assert (handle == null);
assert (fd == null);
ioObject.unplug();
}
@Override
protected void processPlug()
{
ioObject.plug();
if (delayedStart) {
addReconnectTimer();
}
else {
startConnecting();
}
}
@Override
protected void processTerm(int linger)
{
if (timerStarted) {
ioObject.cancelTimer(RECONNECT_TIMER_ID);
timerStarted = false;
}
if (handle != null) {
ioObject.removeHandle(handle);
handle = null;
}
if (fd != null) {
close();
}
super.processTerm(linger);
}
@Override
public void connectEvent()
{
ioObject.removeHandle(handle);
handle = null;
SocketChannel channel = connect();
if (channel == null) {
// Handle the error condition by attempt to reconnect.
close();
addReconnectTimer();
return;
}
try {
TcpUtils.tuneTcpSocket(channel);
TcpUtils.tuneTcpKeepalives(
channel,
options.tcpKeepAlive,
options.tcpKeepAliveCnt,
options.tcpKeepAliveIdle,
options.tcpKeepAliveIntvl);
}
catch (IOException e) {
throw new ZError.IOException(e);
}
// remember our fd for ZMQ_SRCFD in messages
// socket.setFd(channel);
// Create the engine object for this connection.
StreamEngine engine;
try {
engine = new StreamEngine(channel, options, addr.toString());
}
catch (ZError.InstantiationException e) {
// TODO V4 socket.eventConnectDelayed(addr.toString(), -1);
return;
}
this.fd = null;
// Attach the engine to the corresponding session object.
sendAttach(session, engine);
// Shut the connecter down.
terminate();
socket.eventConnected(addr.toString(), channel);
}
@Override
public void timerEvent(int id)
{
assert (id == RECONNECT_TIMER_ID);
timerStarted = false;
startConnecting();
}
// Internal function to start the actual connection establishment.
private void startConnecting()
{
// Open the connecting socket.
try {
boolean rc = open();
// Connect may succeed in synchronous manner.
if (rc) {
handle = ioObject.addFd(fd);
connectEvent();
}
// Connection establishment may be delayed. Poll for its completion.
else {
handle = ioObject.addFd(fd);
ioObject.setPollConnect(handle);
socket.eventConnectDelayed(addr.toString(), -1);
}
}
catch (RuntimeException | IOException e) {
// Handle any other error condition by eventual reconnect.
if (fd != null) {
close();
}
addReconnectTimer();
}
}
// Internal function to add a reconnect timer
private void addReconnectTimer()
{
int rcIvl = getNewReconnectIvl();
ioObject.addTimer(rcIvl, RECONNECT_TIMER_ID);
// resolve address again to take into account other addresses
// besides the failing one (e.g. multiple dns entries).
try {
addr.resolve(options.ipv6);
}
catch (Exception ignored) {
// This will fail if the network goes away and the
// address cannot be resolved for some reason. Try
// not to fail as the event loop will quit
}
socket.eventConnectRetried(addr.toString(), rcIvl);
timerStarted = true;
}
// Internal function to return a reconnect backoff delay.
// Will modify the currentReconnectIvl used for next call
// Returns the currently used interval
private int getNewReconnectIvl()
{
// The new interval is the current interval + random value.
int interval = currentReconnectIvl + (Utils.randomInt() % options.reconnectIvl);
// Only change the current reconnect interval if the maximum reconnect
// interval was set and if it's larger than the reconnect interval.
if (options.reconnectIvlMax > 0 && options.reconnectIvlMax > options.reconnectIvl) {
// Calculate the next interval
currentReconnectIvl = Math.min(currentReconnectIvl * 2, options.reconnectIvlMax);
}
return interval;
}
// Open TCP connecting socket.
// Returns true if connect was successful immediately.
// Returns false if async connect was launched.
private boolean open() throws IOException
{
assert (fd == null);
// Resolve the address
if (addr == null) {
throw new IOException("Null address");
}
addr.resolve(options.ipv6);
Address.IZAddress resolved = addr.resolved();
if (resolved == null) {
throw new IOException("Address not resolved");
}
SocketAddress sa = resolved.address();
if (sa == null) {
throw new IOException("Socket address not resolved");
}
// Create the socket.
if (options.selectorChooser == null) {
fd = SocketChannel.open();
}
else {
fd = options.selectorChooser.choose(resolved, options).openSocketChannel();
}
// On some systems, IPv4 mapping in IPv6 sockets is disabled by default.
// Switch it on in such cases.
// The method enableIpv4Mapping is empty. Still to be written
if (resolved.family() == StandardProtocolFamily.INET6) {
TcpUtils.enableIpv4Mapping(fd);
}
// Set the socket to non-blocking mode so that we get async connect().
TcpUtils.unblockSocket(fd);
// Set the socket buffer limits for the underlying socket.
if (options.sndbuf != 0) {
TcpUtils.setTcpSendBuffer(fd, options.sndbuf);
}
if (options.rcvbuf != 0) {
TcpUtils.setTcpReceiveBuffer(fd, options.rcvbuf);
}
// Set the IP Type-Of-Service priority for this socket
if (options.tos != 0) {
TcpUtils.setIpTypeOfService(fd, options.tos);
}
// TODO V4 Set a source address for conversations
// Connect to the remote peer.
boolean rc;
try {
rc = fd.connect(sa);
if (rc) {
// Connect was successful immediately.
}
else {
// Translate error codes indicating asynchronous connect has been
// launched to a uniform EINPROGRESS.
errno.set(ZError.EINPROGRESS);
}
}
catch (IllegalArgumentException e) {
// this will happen if sa is bad. Address validation is not documented but
// I've found that IAE is thrown in openjdk as well as on android.
throw new IOException(e.getMessage(), e);
}
return rc;
}
// Get the file descriptor of newly created connection. Returns
// null if the connection was unsuccessful.
private SocketChannel connect()
{
try {
// Async connect has finished. Check whether an error occurred
boolean finished = fd.finishConnect();
assert (finished);
return fd;
}
catch (IOException e) {
return null;
}
}
// Close the connecting socket.
protected void close()
{
assert (fd != null);
try {
fd.close();
socket.eventClosed(addr.toString(), fd);
}
catch (IOException e) {
socket.eventCloseFailed(addr.toString(), ZError.exccode(e));
}
fd = null;
}
@Override
public void inEvent()
{
// connected but attaching to stream engine is not completed. do nothing
}
@Override
public void outEvent()
{
// connected but attaching to stream engine is not completed. do nothing
}
@Override
public String toString()
{
return getClass().getSimpleName() + "[" + options.socketId + "]";
}
}
jeromq-0.6.0/src/main/java/zmq/io/net/tcp/TcpListener.java 0000664 0000000 0000000 00000020327 14557711263 0023356 0 ustar 00root root 0000000 0000000 package zmq.io.net.tcp;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Locale;
import zmq.Options;
import zmq.SocketBase;
import zmq.ZError;
import zmq.io.IOObject;
import zmq.io.IOThread;
import zmq.io.SessionBase;
import zmq.io.StreamEngine;
import zmq.io.net.Address.IZAddress;
import zmq.io.net.Listener;
import zmq.io.net.StandardProtocolFamily;
import zmq.poll.Poller;
import zmq.socket.Sockets;
public class TcpListener extends Listener
{
private static final boolean isWindows;
static {
String os = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
isWindows = os.contains("win");
}
// Address to listen on.
private TcpAddress address;
// Underlying socket.
private ServerSocketChannel fd;
private Poller.Handle handle;
// String representation of endpoint to bind to
private String endpoint;
private final IOObject ioObject;
public TcpListener(IOThread ioThread, SocketBase socket, final Options options)
{
super(ioThread, socket, options);
ioObject = new IOObject(ioThread, this);
fd = null;
}
@Override
public void destroy()
{
assert (fd == null);
assert (handle == null);
ioObject.unplug();
}
@Override
protected void processPlug()
{
// Start polling for incoming connections.
ioObject.plug();
handle = ioObject.addFd(fd);
ioObject.setPollAccept(handle);
}
@Override
protected void processTerm(int linger)
{
ioObject.removeHandle(handle);
handle = null;
close();
super.processTerm(linger);
}
@Override
public void acceptEvent()
{
SocketChannel channel;
try {
channel = accept();
// If connection was reset by the peer in the meantime, just ignore it.
if (channel == null) {
socket.eventAcceptFailed(endpoint, ZError.EADDRNOTAVAIL);
return;
}
TcpUtils.tuneTcpSocket(channel);
TcpUtils.tuneTcpKeepalives(
channel,
options.tcpKeepAlive,
options.tcpKeepAliveCnt,
options.tcpKeepAliveIdle,
options.tcpKeepAliveIntvl);
}
catch (IOException e) {
// If connection was reset by the peer in the meantime, just ignore it.
// TODO: Handle specific errors like ENFILE/EMFILE etc.
socket.eventAcceptFailed(endpoint, ZError.exccode(e));
return;
}
// remember our fd for ZMQ_SRCFD in messages
// socket.setFd(channel);
// Create the engine object for this connection.
StreamEngine engine;
try {
engine = new StreamEngine(channel, options, endpoint);
}
catch (ZError.InstantiationException e) {
socket.eventAcceptFailed(endpoint, ZError.EINVAL);
return;
}
// Choose I/O thread to run connecter in. Given that we are already
// running in an I/O thread, there must be at least one available.
IOThread ioThread = chooseIoThread(options.affinity);
assert (ioThread != null);
// Create and launch a session object.
SessionBase session = Sockets.createSession(ioThread, false, socket, options, null);
assert (session != null);
session.incSeqnum();
launchChild(session);
sendAttach(session, engine, false);
socket.eventAccepted(endpoint, channel);
}
// Close the listening socket.
private void close()
{
assert (fd != null);
try {
fd.close();
socket.eventClosed(endpoint, fd);
}
catch (IOException e) {
socket.eventCloseFailed(endpoint, ZError.exccode(e));
}
fd = null;
}
public String getAddress()
{
return address(address);
}
protected String address(IZAddress address)
{
int port = fd.socket().getLocalPort();
return address.toString(port);
}
// Set address to listen on.
public boolean setAddress(final String addr)
{
// Convert the textual address into address structure.
address = new TcpAddress(addr, options.ipv6);
return setAddress();
}
// Set address to listen on, used by IpcListener that already resolved the address.
protected boolean setAddress(InetSocketAddress addr)
{
// Convert the textual address into address structure.
address = new TcpAddress(addr);
return setAddress();
}
private boolean setAddress()
{
endpoint = address.toString();
// Create a listening socket.
try {
if (options.selectorChooser == null) {
fd = ServerSocketChannel.open();
}
else {
fd = options.selectorChooser.choose(address, options).openServerSocketChannel();
}
// On some systems, IPv4 mapping in IPv6 sockets is disabled by default.
// Switch it on in such cases.
// The method enableIpv4Mapping is empty. Still to be written
if (address.family() == StandardProtocolFamily.INET6) {
TcpUtils.enableIpv4Mapping(fd);
}
TcpUtils.unblockSocket(fd);
// Set the socket buffer limits for the underlying socket.
if (options.sndbuf != 0) {
TcpUtils.setTcpSendBuffer(fd, options.sndbuf);
}
if (options.rcvbuf != 0) {
TcpUtils.setTcpReceiveBuffer(fd, options.rcvbuf);
}
if (!isWindows) {
TcpUtils.setReuseAddress(fd, true);
}
// Bind the socket to the network interface and port.
// NB: fd.socket().bind(...) for Android environments
fd.socket().bind(address.address(), options.backlog);
// find the address in case of wildcard
endpoint = getAddress();
}
catch (IOException e) {
close();
errno.set(ZError.EADDRINUSE);
return false;
}
socket.eventListening(endpoint, fd);
return true;
}
// Accept the new connection. Returns the file descriptor of the
// newly created connection. The function may throw IOException
// if the connection was dropped while waiting in the listen backlog
// or was denied because of accept filters.
private SocketChannel accept() throws IOException
{
// The situation where connection cannot be accepted due to insufficient
// resources is considered valid and treated by ignoring the connection.
// Accept one connection and deal with different failure modes.
assert (fd != null);
SocketChannel sock = fd.accept();
if (!options.tcpAcceptFilters.isEmpty()) {
boolean matched = false;
for (TcpAddress.TcpAddressMask am : options.tcpAcceptFilters) {
if (am.matchAddress(address.address())) {
matched = true;
break;
}
}
if (!matched) {
try {
sock.close();
}
catch (IOException ignored) {
// Ignored
}
return null;
}
}
if (options.tos != 0) {
TcpUtils.setIpTypeOfService(sock, options.tos);
}
// Set the socket buffer limits for the underlying socket.
if (options.sndbuf != 0) {
TcpUtils.setTcpSendBuffer(sock, options.sndbuf);
}
if (options.rcvbuf != 0) {
TcpUtils.setTcpReceiveBuffer(sock, options.rcvbuf);
}
if (!isWindows) {
TcpUtils.setReuseAddress(sock, true);
}
return sock;
}
@Override
public String toString()
{
return getClass().getSimpleName() + "[" + options.socketId + "]";
}
}
jeromq-0.6.0/src/main/java/zmq/io/net/tcp/TcpUtils.java 0000664 0000000 0000000 00000012516 14557711263 0022672 0 ustar 00root root 0000000 0000000 package zmq.io.net.tcp;
import java.io.IOException;
import java.net.SocketOption;
import java.net.StandardSocketOptions;
import java.nio.channels.Channel;
import java.nio.channels.NetworkChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SocketChannel;
import zmq.ZError;
import zmq.io.net.Address;
import zmq.util.Utils;
public class TcpUtils
{
public static final boolean WITH_EXTENDED_KEEPALIVE = SocketOptionsProvider.WITH_EXTENDED_KEEPALIVE;
@SuppressWarnings("unchecked")
private static final class SocketOptionsProvider
{
// Wrapped in an inner class, to avoid the @SuppressWarnings for the whole class
private static final SocketOption TCP_KEEPCOUNT;
private static final SocketOption TCP_KEEPIDLE;
private static final SocketOption TCP_KEEPINTERVAL;
private static final boolean WITH_EXTENDED_KEEPALIVE;
static {
SocketOption tryCount = null;
SocketOption tryIdle = null;
SocketOption tryInterval = null;
boolean extendedKeepAlive = false;
try {
Class> eso = TcpUtils.class.getClassLoader().loadClass("jdk.net.ExtendedSocketOptions");
tryCount = (SocketOption) eso.getField("TCP_KEEPCOUNT").get(null);
tryIdle = (SocketOption) eso.getField("TCP_KEEPIDLE").get(null);
tryInterval = (SocketOption) eso.getField("TCP_KEEPINTERVAL").get(null);
extendedKeepAlive = true;
}
catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
// If failing, will keep extendedKeepAlive to false
}
TCP_KEEPCOUNT = tryCount;
TCP_KEEPIDLE = tryIdle;
TCP_KEEPINTERVAL = tryInterval;
WITH_EXTENDED_KEEPALIVE = extendedKeepAlive;
}
}
private TcpUtils()
{
}
// The explicit IOException is useless, but kept for API compatibility
public static void tuneTcpSocket(Channel channel) throws IOException
{
// Disable Nagle's algorithm. We are doing data batching on 0MQ level,
// so using Nagle wouldn't improve throughput in any way, but it would
// hurt latency.
setOption(channel, StandardSocketOptions.TCP_NODELAY, true);
}
public static void tuneTcpKeepalives(Channel channel, int tcpKeepAlive, int tcpKeepAliveCnt,
int tcpKeepAliveIdle, int tcpKeepAliveIntvl)
{
if (tcpKeepAlive != -1) {
if (channel instanceof SocketChannel) {
setOption(channel, StandardSocketOptions.SO_KEEPALIVE, tcpKeepAlive == 1);
}
if (WITH_EXTENDED_KEEPALIVE && tcpKeepAlive == 1) {
if (tcpKeepAliveCnt > 0) {
setOption(channel, SocketOptionsProvider.TCP_KEEPCOUNT, tcpKeepAliveCnt);
}
if (tcpKeepAliveIdle > 0) {
setOption(channel, SocketOptionsProvider.TCP_KEEPIDLE, tcpKeepAliveIdle);
}
if (tcpKeepAliveIntvl > 0) {
setOption(channel, SocketOptionsProvider.TCP_KEEPINTERVAL, tcpKeepAliveIntvl);
}
}
}
}
public static boolean setTcpReceiveBuffer(Channel channel, final int rcvbuf)
{
setOption(channel, StandardSocketOptions.SO_RCVBUF, rcvbuf);
return true;
}
public static boolean setTcpSendBuffer(Channel channel, final int sndbuf)
{
setOption(channel, StandardSocketOptions.SO_SNDBUF, sndbuf);
return true;
}
public static boolean setIpTypeOfService(Channel channel, final int tos)
{
setOption(channel, StandardSocketOptions.SO_SNDBUF, tos);
return true;
}
public static boolean setReuseAddress(Channel channel, final boolean reuse)
{
setOption(channel, StandardSocketOptions.SO_REUSEADDR, reuse);
return true;
}
private static void setOption(Channel channel, SocketOption option, T value)
{
try {
if (channel instanceof NetworkChannel) {
((NetworkChannel) channel).setOption(option, value);
}
else {
throw new IllegalArgumentException("Channel " + channel + " is not a network channel");
}
}
catch (IOException e) {
throw new ZError.IOException(e);
}
}
public static void unblockSocket(SelectableChannel... channels) throws IOException
{
for (SelectableChannel ch : channels) {
ch.configureBlocking(false);
}
}
public static void enableIpv4Mapping(SelectableChannel channel)
{
// TODO V4 enable ipv4 mapping
}
/**
* Return the {@link Address} of the channel
* @param channel the channel, should be a TCP socket channel
* @return The {@link Address} of the channel
* @deprecated Use {@link zmq.util.Utils#getPeerIpAddress(SocketChannel)} instead
* @throws ZError.IOException if the channel is closed or an I/O errors occurred
* @throws IllegalArgumentException if the SocketChannel is not a TCP channel
*/
@Deprecated
public static Address getPeerIpAddress(SocketChannel channel)
{
return Utils.getPeerIpAddress(channel);
}
}
jeromq-0.6.0/src/main/java/zmq/io/net/tipc/ 0000775 0000000 0000000 00000000000 14557711263 0020424 5 ustar 00root root 0000000 0000000 jeromq-0.6.0/src/main/java/zmq/io/net/tipc/TipcConnecter.java 0000664 0000000 0000000 00000001010 14557711263 0024017 0 ustar 00root root 0000000 0000000 package zmq.io.net.tipc;
import zmq.Options;
import zmq.io.IOThread;
import zmq.io.SessionBase;
import zmq.io.net.Address;
import zmq.io.net.tcp.TcpConnecter;
public class TipcConnecter extends TcpConnecter
{
public TipcConnecter(IOThread ioThread, SessionBase session, final Options options, final Address addr,
boolean wait)
{
super(ioThread, session, options, addr, wait);
// TODO V4 implement Tipc
throw new UnsupportedOperationException("TODO implement Tipc");
}
}
jeromq-0.6.0/src/main/java/zmq/io/net/tipc/TipcListener.java 0000664 0000000 0000000 00000000650 14557711263 0023675 0 ustar 00root root 0000000 0000000 package zmq.io.net.tipc;
import zmq.Options;
import zmq.SocketBase;
import zmq.io.IOThread;
import zmq.io.net.tcp.TcpListener;
public class TipcListener extends TcpListener
{
public TipcListener(IOThread ioThread, SocketBase socket, final Options options)
{
super(ioThread, socket, options);
// TODO V4 implement tipc
throw new UnsupportedOperationException("TODO implement tipc");
}
}
jeromq-0.6.0/src/main/java/zmq/msg/ 0000775 0000000 0000000 00000000000 14557711263 0017056 5 ustar 00root root 0000000 0000000 jeromq-0.6.0/src/main/java/zmq/msg/MsgAllocator.java 0000664 0000000 0000000 00000000141 14557711263 0022304 0 ustar 00root root 0000000 0000000 package zmq.msg;
import zmq.Msg;
public interface MsgAllocator
{
Msg allocate(int size);
}
jeromq-0.6.0/src/main/java/zmq/msg/MsgAllocatorDirect.java 0000664 0000000 0000000 00000000361 14557711263 0023443 0 ustar 00root root 0000000 0000000 package zmq.msg;
import java.nio.ByteBuffer;
import zmq.Msg;
public class MsgAllocatorDirect implements MsgAllocator
{
@Override
public Msg allocate(int size)
{
return new Msg(ByteBuffer.allocateDirect(size));
}
}
jeromq-0.6.0/src/main/java/zmq/msg/MsgAllocatorHeap.java 0000664 0000000 0000000 00000000267 14557711263 0023113 0 ustar 00root root 0000000 0000000 package zmq.msg;
import zmq.Msg;
public class MsgAllocatorHeap implements MsgAllocator
{
@Override
public Msg allocate(int size)
{
return new Msg(size);
}
}
jeromq-0.6.0/src/main/java/zmq/msg/MsgAllocatorThreshold.java 0000664 0000000 0000000 00000001331 14557711263 0024163 0 ustar 00root root 0000000 0000000 package zmq.msg;
import zmq.Config;
import zmq.Msg;
public class MsgAllocatorThreshold implements MsgAllocator
{
private static final MsgAllocator direct = new MsgAllocatorDirect();
private static final MsgAllocator heap = new MsgAllocatorHeap();
public final int threshold;
public MsgAllocatorThreshold()
{
this(Config.MSG_ALLOCATION_HEAP_THRESHOLD.getValue());
}
public MsgAllocatorThreshold(int threshold)
{
this.threshold = threshold;
}
@Override
public Msg allocate(int size)
{
if (threshold > 0 && size > threshold) {
return direct.allocate(size);
}
else {
return heap.allocate(size);
}
}
}
jeromq-0.6.0/src/main/java/zmq/msg/package-info.java 0000664 0000000 0000000 00000000552 14557711263 0022247 0 ustar 00root root 0000000 0000000 /**
* Provides utility for message allocation within ØMQ.
*
* This is a java-only construct, allowing to customize the creation of messages (potentially sharing buffers, for instance).
*
* The classes of this package shall be used with {@link zmq.ZMQ#ZMQ_MSG_ALLOCATOR} or {@link zmq.ZMQ#ZMQ_MSG_ALLOCATION_HEAP_THRESHOLD}
*/
package zmq.msg;
jeromq-0.6.0/src/main/java/zmq/package-info.java 0000664 0000000 0000000 00000001445 14557711263 0021463 0 ustar 00root root 0000000 0000000 /**
* Provides low-level bindings for ØMQ.
*
* This is the java equivalent of