package cz.cuni.mff.d3s.been.mq;
import java.io.Serializable;
import org.apache.commons.lang3.SerializationException;
import org.apache.commons.lang3.SerializationUtils;
import org.jeromq.ZMQ;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cz.cuni.mff.d3s.been.annotation.NotThreadSafe;
/**
* Receiver for a queue in inter-process communication.
*
* The queue can send/receive only one type of an object, specified by the type
* parameter.
*
* We do not care who are the senders. The receiver's responsibility is just to
* provide received messages to a higher level.
*
* Implementation notes: Implemented using 0MQ PUSH-PULL model in 0MQ. This is
* the PULL part.
*
* @see cz.cuni.mff.d3s.been.mq.InprocMessageReceiver
* @author Martin Sixta
*/
@NotThreadSafe
final class InprocMessageReceiver<T extends Serializable> implements IMessageReceiver<T> {
/** Logging */
private static Logger log = LoggerFactory.getLogger(InprocMessageReceiver.class);
/**
* ZMQ.Context to use for the connection.
*/
private final ZMQContext context;
/**
* Name of the queue.
*/
private final String queue;
/**
* ZMQ.Socket to communicate with.
*/
private ZMQ.Socket socket;
/**
* Connection string.
*/
private String INPROC_CONN;
/**
* Denotes success from a ZMQ.Socket.bind call
*/
private static final int PORT_OK = 0;
/**
* Creates a new receiver
*
* The receiver is not bind to its queue and call to {@link #bind()} is
* required before receiving any messages.
*
* @param context Context to create the receiver in
* @param queue Queue the receiver will listen on
*/
public InprocMessageReceiver(final ZMQContext context, final String queue) {
this.context = context;
this.queue = queue;
INPROC_CONN = Messaging.createInprocConnection(queue);
}
public void bind() throws MessagingException {
if (isConnected()) {
return; // already connected
}
socket = context.socket(ZMQ.PULL);
int port = socket.bind(INPROC_CONN);
if (port != PORT_OK) {
socket = null;
throw new MessagingException("Cannot bind socket.");
}
log.debug("IMessageReceiver is bind to address {} ", INPROC_CONN);
}
@Override
public T receive() throws MessagingException {
if (!isConnected()) {
throw new MessagingException("Receive on unbind socket.");
}
try {
byte[] bytes = socket.recv();
Object object = SerializationUtils.deserialize(bytes);
return (T) object;
} catch (ClassCastException | SerializationException | IllegalArgumentException e) {
throw new MessagingException("Cannot cast to a proper type.", e);
}
}
/**
* Returns connection status.
*
* @return true if the sender is connected to its queue, false otherwise
*/
@Override
public boolean isConnected() {
return socket != null;
}
@Override
public int getPort() {
return PORT_OK;
}
@Override
public void close() {
if (isConnected()) {
socket.close();
socket = null;
}
}
public InprocMessageSender<T> createSender() {
return new InprocMessageSender<>(context, queue);
}
}