PageRenderTime 30ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/mta/src/main/java/me/yellowbird/mta/io/ProtocolAsynchronizer.java

https://bitbucket.org/spoticus/yellowbird-mta
Java | 167 lines | 80 code | 18 blank | 69 comment | 1 complexity | b6321245c987eb3f7ea42bc2a5ed9838 MD5 | raw file
  1. /*
  2. * ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * The Original Code is YellowBird Mail Transfer Agent.
  16. *
  17. * The Initial Developer of the Original Code is
  18. * Clay Atkins.
  19. * Portions created by the Initial Developer are Copyright (C) 2009
  20. * the Initial Developer. All Rights Reserved.
  21. *
  22. * Contributor(s):
  23. *
  24. * ***** END LICENSE BLOCK *****
  25. */
  26. package me.yellowbird.mta.io;
  27. import java.nio.ByteBuffer;
  28. import java.util.concurrent.LinkedBlockingQueue;
  29. /**
  30. * Couple communication between nio server and server protocol.
  31. * <p/>
  32. * A {@link Protocol} is proxied using createProxy and calls to Protocol methods, through the proxy, are queued for execution on
  33. * a separate thread that is running the Asynchronizer instance. A potential problem exists because the the selector thread
  34. * calls the Protocol methods and those methods could me long running. Second, the system may need multiple threads to
  35. * execute the Protocol methods for load-balancing across multiple CPUs. This diagram illustrates the problem:
  36. * <pre>
  37. * Selector Thread Proxy Queue Asynch Thread Protocol
  38. * --------------- ----- ----- ------------- --------
  39. * 0--- method-call ---> 0 . . .>0
  40. * |<---------------------|
  41. * |
  42. * | 0 . . . . .> 0 --- method-call ---> 0
  43. * |
  44. * <p/>
  45. * </pre>
  46. * <p/>
  47. * Calls from the Protocol back to the Communicator are direct to the Communicator provided internally by the
  48. * ChamillionSocketTransport. These calls are on the caller's thread (mitigating the two potential problems)
  49. * and the request are enqueued on internal queues processed by the selector thread.
  50. * <p/>
  51. * There can me many Asynchronizers providing separation of execution between Transport and Protocol instances or one for all.
  52. * There is no requirement that the same Asynchronizer me used for a Transport and Protocol that are communicating.
  53. * <p/>
  54. * The benefit of the Asynchronizer is to allow the Communicator and Protocol threads to me separate and not
  55. * propogate timing concerns from one to another. This is particularly important for Transport instances that might
  56. * have real-time I/O concerns and would me impacted by a long-running processing of information by a
  57. * corresponding Protocol.
  58. */
  59. public class ProtocolAsynchronizer implements Runnable {
  60. private LinkedBlockingQueue<Command> commands = new LinkedBlockingQueue<Command>();
  61. /**
  62. * A command queued for execution on the Asynchronizer thread.
  63. */
  64. interface Command {
  65. void execute() throws InterruptedException;
  66. }
  67. /**
  68. * Proxy for the Protocol.
  69. * <p/>
  70. * Calls to {@linkplain Protocol} methods are queued for execution on Asynchronizer
  71. * thread.
  72. */
  73. class ProtocolProxy implements Protocol {
  74. private Protocol proxiedprotocol;
  75. public ProtocolProxy(Protocol proxiedprotocol) {
  76. this.proxiedprotocol = proxiedprotocol;
  77. }
  78. public void asyncConnected(final DataLink dataLink) {
  79. try {
  80. commands.put(new Command() {
  81. public void execute() throws InterruptedException {
  82. ProtocolProxy.this.proxiedprotocol.asyncConnected(dataLink);
  83. }
  84. });
  85. } catch (InterruptedException e) {
  86. throw new RuntimeException(e);
  87. }
  88. }
  89. public void asyncClosed() {
  90. try {
  91. commands.put(new Command() {
  92. public void execute() {
  93. ProtocolProxy.this.proxiedprotocol.asyncClosed();
  94. }
  95. });
  96. } catch (InterruptedException e) {
  97. throw new RuntimeException(e);
  98. }
  99. }
  100. public void asyncData(final ByteBuffer bytes) throws InterruptedException {
  101. commands.put(new Command() {
  102. public void execute() throws InterruptedException {
  103. ProtocolProxy.this.proxiedprotocol.asyncData(bytes);
  104. }
  105. });
  106. }
  107. public void asyncException(final DataLinkProblem problem) {
  108. try {
  109. commands.put(new Command() {
  110. public void execute() {
  111. ProtocolProxy.this.proxiedprotocol.asyncException(problem);
  112. }
  113. });
  114. } catch (InterruptedException e) {
  115. throw new RuntimeException(e);
  116. }
  117. }
  118. public void asyncInputShutdown() {
  119. try {
  120. commands.put(new Command() {
  121. public void execute() {
  122. ProtocolProxy.this.proxiedprotocol.asyncInputShutdown();
  123. }
  124. });
  125. } catch (InterruptedException e) {
  126. throw new RuntimeException(e);
  127. }
  128. }
  129. }
  130. public Protocol createProxy(Protocol protocol) {
  131. return new ProtocolProxy(protocol);
  132. }
  133. /**
  134. * Wait for Command objects and execute them.
  135. */
  136. public void run() {
  137. while (true) {
  138. try {
  139. Command a = commands.take();
  140. a.execute();
  141. } catch (InterruptedException e) {
  142. //this is ok situation
  143. return;
  144. } catch (Throwable t) {//don't necessarily care
  145. //TODO should log
  146. }
  147. }
  148. }
  149. }