001 /*
002 * Created on 20-May-2004
003 */
004 package ca.uhn.hl7v2.protocol.impl;
005
006 import ca.uhn.hl7v2.protocol.TransportException;
007 import ca.uhn.hl7v2.protocol.TransportLayer;
008
009 /**
010 * <p>A utility for connecting separate inbound and outbound
011 * <code>TransortLayer</code>s in a manner that avoids deadlock.</p>
012 *
013 * <p>It is not safe to call connect() on two <code>TransportLayer</code>
014 * in the same thread, because it blocks, and the remote system may be doing
015 * the same thing, but in the opposite order. This class provides a method
016 * that connects two layers in separate threads, and pends until they are
017 * both connected.</p>
018 *
019 * @author <a href="mailto:bryan.tripp@uhn.on.ca">Bryan Tripp</a>
020 * @version $Revision: 1.1 $ updated on $Date: 2007-02-19 02:24:26 $ by $Author: jamesagnew $
021 */
022 public class DualTransportConnector {
023
024 private final TransportLayer myTransportA;
025 private final TransportLayer myTransportB;
026 private boolean isConnecting;
027
028 /**
029 * @param theTransportA one <code>TransportLayer</code> we will want to connect
030 * @param theTransportB another one
031 */
032 public DualTransportConnector(TransportLayer theTransportA, TransportLayer theTransportB) {
033 myTransportA = theTransportA;
034 myTransportB = theTransportB;
035 }
036
037 /**
038 * @return one of the underlying <code>TransportLayer</code>s.
039 */
040 public TransportLayer getTransportA() {
041 return myTransportA;
042 }
043
044 /**
045 * @return the other underlying <code>TransportLayer</code>.
046 */
047 public TransportLayer getTransportB() {
048 return myTransportB;
049 }
050
051 /**
052 * Connects both <code>TransportLayer</code>s in separate threads,
053 * and returns when both have been connected, or when cancelConnect()
054 * is called.
055 */
056 public void connect() throws TransportException {
057 isConnecting = true;
058 ConnectThread c1 = new ConnectThread(myTransportA);
059 ConnectThread c2 = new ConnectThread(myTransportB);
060 c1.start();
061 c2.start();
062
063 while (isConnecting
064 && (!c1.isConnected() || !c2.isConnected())
065 && c1.getException() == null
066 && c2.getException() == null) {
067
068 try {
069 Thread.sleep(1);
070 } catch (InterruptedException e) {}
071 }
072
073 if (c1.getException() != null) throw c1.getException();
074 if (c2.getException() != null) throw c2.getException();
075 }
076
077 public void disconnect() throws TransportException {
078 myTransportA.disconnect();
079 myTransportB.disconnect();
080 }
081
082 /**
083 * Cancels a connect() in progress. Since connect() blocks, this must
084 * be called from a separate thread.
085 */
086 public void cancelConnect() {
087 isConnecting = false;
088 }
089
090 /**
091 * A class to facilitate connecting a <code>TransportLayer</code> in
092 * a separate thread. This is needed when we want to perform two connections
093 * that are initiated remotely, and we don't know the order in which the
094 * remote system will initiate the connections.
095 *
096 * @author <a href="mailto:bryan.tripp@uhn.on.ca">Bryan Tripp</a>
097 * @version $Revision: 1.1 $ updated on $Date: 2007-02-19 02:24:26 $ by $Author: jamesagnew $
098 */
099 private static class ConnectThread extends Thread {
100
101 private TransportLayer myTransport;
102 private TransportException myException;
103
104 public ConnectThread(TransportLayer theTransport) {
105 myTransport = theTransport;
106 }
107
108 public boolean isConnected() {
109 return myTransport.isConnected();
110 }
111
112 /**
113 * @return an exception encountered during the last run, if any
114 */
115 public TransportException getException() {
116 return myException;
117 }
118
119 public void run() {
120 myException = null;
121 try {
122 myTransport.connect();
123 } catch (TransportException e) {
124 myException = e;
125 }
126 }
127 }
128
129 }