MessageControl.java 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. package com.serotonin.cdc.messaging;
  2. import java.io.IOException;
  3. import com.serotonin.cdc.io.StreamUtils;
  4. import com.serotonin.cdc.util.queue.ByteQueue;
  5. /**
  6. * In general there are three messaging activities:
  7. * <ol>
  8. * <li>Send a message for which no reply is expected, e.g. a broadcast.</li>
  9. * <li>Send a message and wait for a response with timeout and retries.</li>
  10. * <li>Listen for unsolicited requests.</li>
  11. * </ol>
  12. *
  13. * @author Matthew Lohbihler
  14. */
  15. public class MessageControl implements DataConsumer {
  16. private static int DEFAULT_RETRIES = 2;
  17. private static int DEFAULT_TIMEOUT = 500;
  18. public boolean DEBUG = false;
  19. private Transport transport;
  20. private MessageParser messageParser;
  21. private RequestHandler requestHandler;
  22. private WaitingRoomKeyFactory waitingRoomKeyFactory;
  23. private MessagingExceptionHandler exceptionHandler = new DefaultMessagingExceptionHandler();
  24. private int retries = DEFAULT_RETRIES;
  25. private int timeout = DEFAULT_TIMEOUT;
  26. private int discardDataDelay = 0;
  27. private long lastDataTimestamp;
  28. private final WaitingRoom waitingRoom = new WaitingRoom();
  29. private final ByteQueue dataBuffer = new ByteQueue();
  30. public void start(Transport transport, MessageParser messageParser, RequestHandler handler,
  31. WaitingRoomKeyFactory waitingRoomKeyFactory) throws IOException {
  32. this.transport = transport;
  33. this.messageParser = messageParser;
  34. this.requestHandler = handler;
  35. this.waitingRoomKeyFactory = waitingRoomKeyFactory;
  36. waitingRoom.setKeyFactory(waitingRoomKeyFactory);
  37. transport.setConsumer(this);
  38. }
  39. public void close() {
  40. transport.removeConsumer();
  41. }
  42. public void setExceptionHandler(MessagingExceptionHandler exceptionHandler) {
  43. if (exceptionHandler == null)
  44. this.exceptionHandler = new DefaultMessagingExceptionHandler();
  45. else
  46. this.exceptionHandler = exceptionHandler;
  47. }
  48. public int getRetries() {
  49. return retries;
  50. }
  51. public void setRetries(int retries) {
  52. this.retries = retries;
  53. }
  54. public int getTimeout() {
  55. return timeout;
  56. }
  57. public void setTimeout(int timeout) {
  58. this.timeout = timeout;
  59. }
  60. public int getDiscardDataDelay() {
  61. return discardDataDelay;
  62. }
  63. public void setDiscardDataDelay(int discardDataDelay) {
  64. this.discardDataDelay = discardDataDelay;
  65. }
  66. public IncomingResponseMessage send(OutgoingRequestMessage request) throws IOException {
  67. return send(request, timeout, retries);
  68. }
  69. public IncomingResponseMessage send(OutgoingRequestMessage request, int timeout, int retries) throws IOException {
  70. byte[] data = request.getMessageData();
  71. if (DEBUG)
  72. System.out.println("MessagingControl.send: " + StreamUtils.dumpMessage(data));
  73. IncomingResponseMessage response = null;
  74. if (request.expectsResponse()) {
  75. WaitingRoomKey key = waitingRoomKeyFactory.createWaitingRoomKey(request);
  76. // Enter the waiting room
  77. waitingRoom.enter(key);
  78. try {
  79. do {
  80. // Send the request.
  81. write(data);
  82. // Wait for the response.
  83. response = waitingRoom.getResponse(key, timeout);
  84. if (DEBUG && response == null)
  85. System.out.println("Timeout waiting for response");
  86. }
  87. while (response == null && retries-- > 0);
  88. }
  89. finally {
  90. // Leave the waiting room.
  91. waitingRoom.leave(key);
  92. }
  93. if (response == null)
  94. throw new TimeoutException("request=" + request);
  95. }
  96. else
  97. write(data);
  98. return response;
  99. }
  100. public void send(OutgoingResponseMessage response) throws IOException {
  101. write(response.getMessageData());
  102. }
  103. /**
  104. * Incoming data from the transport. Single-threaded.
  105. */
  106. public void data(byte[] b, int len) {
  107. if (DEBUG)
  108. System.out.println("MessagingConnection.read: " + StreamUtils.dumpMessage(b, 0, len));
  109. if (discardDataDelay > 0) {
  110. long now = System.currentTimeMillis();
  111. if (now - lastDataTimestamp > discardDataDelay)
  112. dataBuffer.clear();
  113. lastDataTimestamp = now;
  114. }
  115. dataBuffer.push(b, 0, len);
  116. // There may be multiple messages in the data, so enter a loop.
  117. while (true) {
  118. // Attempt to parse a message.
  119. try {
  120. // Mark where we are in the buffer. The entire message may not be in yet, but since the parser
  121. // will consume the buffer we need to be able to backtrack.
  122. dataBuffer.mark();
  123. IncomingMessage message = messageParser.parseMessage(dataBuffer);
  124. if (message == null) {
  125. // Nothing to do. Reset the buffer and exit the loop.
  126. dataBuffer.reset();
  127. break;
  128. }
  129. if (message instanceof IncomingRequestMessage) {
  130. // Received a request. Give it to the request handler
  131. if (requestHandler != null) {
  132. OutgoingResponseMessage response = requestHandler
  133. .handleRequest((IncomingRequestMessage) message);
  134. if (response != null)
  135. send(response);
  136. }
  137. }
  138. else
  139. // Must be a response. Give it to the waiting room.
  140. waitingRoom.response((IncomingResponseMessage) message);
  141. }
  142. catch (Exception e) {
  143. exceptionHandler.receivedException(e);
  144. }
  145. }
  146. }
  147. private void write(byte[] data) throws IOException {
  148. synchronized (transport) {
  149. transport.write(data);
  150. }
  151. }
  152. public void handleIOException(IOException e) {
  153. exceptionHandler.receivedException(e);
  154. }
  155. }