All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.zeromq.ZMsg Maven / Gradle / Ivy

The newest version!
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 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(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy