| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- package com.serotonin.cdc.messaging;
- import java.io.IOException;
- import com.serotonin.cdc.io.StreamUtils;
- import com.serotonin.cdc.util.queue.ByteQueue;
- /**
- * In general there are three messaging activities:
- * <ol>
- * <li>Send a message for which no reply is expected, e.g. a broadcast.</li>
- * <li>Send a message and wait for a response with timeout and retries.</li>
- * <li>Listen for unsolicited requests.</li>
- * </ol>
- *
- * @author Matthew Lohbihler
- */
- public class MessageControl implements DataConsumer {
- private static int DEFAULT_RETRIES = 2;
- private static int DEFAULT_TIMEOUT = 500;
- public boolean DEBUG = false;
- private Transport transport;
- private MessageParser messageParser;
- private RequestHandler requestHandler;
- private WaitingRoomKeyFactory waitingRoomKeyFactory;
- private MessagingExceptionHandler exceptionHandler = new DefaultMessagingExceptionHandler();
- private int retries = DEFAULT_RETRIES;
- private int timeout = DEFAULT_TIMEOUT;
- private int discardDataDelay = 0;
- private long lastDataTimestamp;
- private final WaitingRoom waitingRoom = new WaitingRoom();
- private final ByteQueue dataBuffer = new ByteQueue();
- public void start(Transport transport, MessageParser messageParser, RequestHandler handler,
- WaitingRoomKeyFactory waitingRoomKeyFactory) throws IOException {
- this.transport = transport;
- this.messageParser = messageParser;
- this.requestHandler = handler;
- this.waitingRoomKeyFactory = waitingRoomKeyFactory;
- waitingRoom.setKeyFactory(waitingRoomKeyFactory);
- transport.setConsumer(this);
- }
- public void close() {
- transport.removeConsumer();
- }
- public void setExceptionHandler(MessagingExceptionHandler exceptionHandler) {
- if (exceptionHandler == null)
- this.exceptionHandler = new DefaultMessagingExceptionHandler();
- else
- this.exceptionHandler = exceptionHandler;
- }
- public int getRetries() {
- return retries;
- }
- public void setRetries(int retries) {
- this.retries = retries;
- }
- public int getTimeout() {
- return timeout;
- }
- public void setTimeout(int timeout) {
- this.timeout = timeout;
- }
- public int getDiscardDataDelay() {
- return discardDataDelay;
- }
- public void setDiscardDataDelay(int discardDataDelay) {
- this.discardDataDelay = discardDataDelay;
- }
- public IncomingResponseMessage send(OutgoingRequestMessage request) throws IOException {
- return send(request, timeout, retries);
- }
- public IncomingResponseMessage send(OutgoingRequestMessage request, int timeout, int retries) throws IOException {
- byte[] data = request.getMessageData();
- if (DEBUG)
- System.out.println("MessagingControl.send: " + StreamUtils.dumpMessage(data));
- IncomingResponseMessage response = null;
- if (request.expectsResponse()) {
- WaitingRoomKey key = waitingRoomKeyFactory.createWaitingRoomKey(request);
- // Enter the waiting room
- waitingRoom.enter(key);
- try {
- do {
- // Send the request.
- write(data);
- // Wait for the response.
- response = waitingRoom.getResponse(key, timeout);
- if (DEBUG && response == null)
- System.out.println("Timeout waiting for response");
- }
- while (response == null && retries-- > 0);
- }
- finally {
- // Leave the waiting room.
- waitingRoom.leave(key);
- }
- if (response == null)
- throw new TimeoutException("request=" + request);
- }
- else
- write(data);
- return response;
- }
- public void send(OutgoingResponseMessage response) throws IOException {
- write(response.getMessageData());
- }
- /**
- * Incoming data from the transport. Single-threaded.
- */
- public void data(byte[] b, int len) {
- if (DEBUG)
- System.out.println("MessagingConnection.read: " + StreamUtils.dumpMessage(b, 0, len));
- if (discardDataDelay > 0) {
- long now = System.currentTimeMillis();
- if (now - lastDataTimestamp > discardDataDelay)
- dataBuffer.clear();
- lastDataTimestamp = now;
- }
- dataBuffer.push(b, 0, len);
- // There may be multiple messages in the data, so enter a loop.
- while (true) {
- // Attempt to parse a message.
- try {
- // Mark where we are in the buffer. The entire message may not be in yet, but since the parser
- // will consume the buffer we need to be able to backtrack.
- dataBuffer.mark();
- IncomingMessage message = messageParser.parseMessage(dataBuffer);
- if (message == null) {
- // Nothing to do. Reset the buffer and exit the loop.
- dataBuffer.reset();
- break;
- }
- if (message instanceof IncomingRequestMessage) {
- // Received a request. Give it to the request handler
- if (requestHandler != null) {
- OutgoingResponseMessage response = requestHandler
- .handleRequest((IncomingRequestMessage) message);
- if (response != null)
- send(response);
- }
- }
- else
- // Must be a response. Give it to the waiting room.
- waitingRoom.response((IncomingResponseMessage) message);
- }
- catch (Exception e) {
- exceptionHandler.receivedException(e);
- }
- }
- }
- private void write(byte[] data) throws IOException {
- synchronized (transport) {
- transport.write(data);
- }
- }
- public void handleIOException(IOException e) {
- exceptionHandler.receivedException(e);
- }
- }
|