/mta/src/main/java/me/yellowbird/mta/io/ProtocolAsynchronizer.java
Java | 167 lines | 80 code | 18 blank | 69 comment | 1 complexity | b6321245c987eb3f7ea42bc2a5ed9838 MD5 | raw file
- /*
- * ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is YellowBird Mail Transfer Agent.
- *
- * The Initial Developer of the Original Code is
- * Clay Atkins.
- * Portions created by the Initial Developer are Copyright (C) 2009
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * ***** END LICENSE BLOCK *****
- */
-
- package me.yellowbird.mta.io;
-
-
- import java.nio.ByteBuffer;
- import java.util.concurrent.LinkedBlockingQueue;
-
- /**
- * Couple communication between nio server and server protocol.
- * <p/>
- * A {@link Protocol} is proxied using createProxy and calls to Protocol methods, through the proxy, are queued for execution on
- * a separate thread that is running the Asynchronizer instance. A potential problem exists because the the selector thread
- * calls the Protocol methods and those methods could me long running. Second, the system may need multiple threads to
- * execute the Protocol methods for load-balancing across multiple CPUs. This diagram illustrates the problem:
- * <pre>
- * Selector Thread Proxy Queue Asynch Thread Protocol
- * --------------- ----- ----- ------------- --------
- * 0--- method-call ---> 0 . . .>0
- * |<---------------------|
- * |
- * | 0 . . . . .> 0 --- method-call ---> 0
- * |
- * <p/>
- * </pre>
- * <p/>
- * Calls from the Protocol back to the Communicator are direct to the Communicator provided internally by the
- * ChamillionSocketTransport. These calls are on the caller's thread (mitigating the two potential problems)
- * and the request are enqueued on internal queues processed by the selector thread.
- * <p/>
- * There can me many Asynchronizers providing separation of execution between Transport and Protocol instances or one for all.
- * There is no requirement that the same Asynchronizer me used for a Transport and Protocol that are communicating.
- * <p/>
- * The benefit of the Asynchronizer is to allow the Communicator and Protocol threads to me separate and not
- * propogate timing concerns from one to another. This is particularly important for Transport instances that might
- * have real-time I/O concerns and would me impacted by a long-running processing of information by a
- * corresponding Protocol.
- */
- public class ProtocolAsynchronizer implements Runnable {
- private LinkedBlockingQueue<Command> commands = new LinkedBlockingQueue<Command>();
-
- /**
- * A command queued for execution on the Asynchronizer thread.
- */
- interface Command {
- void execute() throws InterruptedException;
- }
-
- /**
- * Proxy for the Protocol.
- * <p/>
- * Calls to {@linkplain Protocol} methods are queued for execution on Asynchronizer
- * thread.
- */
- class ProtocolProxy implements Protocol {
- private Protocol proxiedprotocol;
-
- public ProtocolProxy(Protocol proxiedprotocol) {
- this.proxiedprotocol = proxiedprotocol;
- }
-
- public void asyncConnected(final DataLink dataLink) {
- try {
- commands.put(new Command() {
- public void execute() throws InterruptedException {
- ProtocolProxy.this.proxiedprotocol.asyncConnected(dataLink);
- }
- });
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
-
- public void asyncClosed() {
- try {
- commands.put(new Command() {
- public void execute() {
- ProtocolProxy.this.proxiedprotocol.asyncClosed();
- }
- });
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
-
- public void asyncData(final ByteBuffer bytes) throws InterruptedException {
- commands.put(new Command() {
- public void execute() throws InterruptedException {
- ProtocolProxy.this.proxiedprotocol.asyncData(bytes);
- }
- });
- }
-
- public void asyncException(final DataLinkProblem problem) {
- try {
- commands.put(new Command() {
- public void execute() {
- ProtocolProxy.this.proxiedprotocol.asyncException(problem);
- }
- });
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
-
- public void asyncInputShutdown() {
- try {
- commands.put(new Command() {
- public void execute() {
- ProtocolProxy.this.proxiedprotocol.asyncInputShutdown();
- }
- });
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- public Protocol createProxy(Protocol protocol) {
- return new ProtocolProxy(protocol);
- }
-
- /**
- * Wait for Command objects and execute them.
- */
- public void run() {
-
- while (true) {
- try {
- Command a = commands.take();
-
- a.execute();
-
- } catch (InterruptedException e) {
- //this is ok situation
- return;
- } catch (Throwable t) {//don't necessarily care
- //TODO should log
- }
- }
- }
-
- }