1 /*
2 * Created on 20-May-2004
3 */
4 package ca.uhn.hl7v2.protocol.impl;
5
6 import ca.uhn.hl7v2.protocol.TransportException;
7 import ca.uhn.hl7v2.protocol.TransportLayer;
8
9 /**
10 * <p>A utility for connecting separate inbound and outbound
11 * <code>TransortLayer</code>s in a manner that avoids deadlock.</p>
12 *
13 * <p>It is not safe to call connect() on two <code>TransportLayer</code>
14 * in the same thread, because it blocks, and the remote system may be doing
15 * the same thing, but in the opposite order. This class provides a method
16 * that connects two layers in separate threads, and pends until they are
17 * both connected.</p>
18 *
19 * @author <a href="mailto:bryan.tripp@uhn.on.ca">Bryan Tripp</a>
20 * @version $Revision: 1.1 $ updated on $Date: 2007-02-19 02:24:26 $ by $Author: jamesagnew $
21 */
22 public class DualTransportConnector {
23
24 private final TransportLayer myTransportA;
25 private final TransportLayer myTransportB;
26 private boolean isConnecting;
27
28 /**
29 * @param theTransportA one <code>TransportLayer</code> we will want to connect
30 * @param theTransportB another one
31 */
32 public DualTransportConnector(TransportLayer./ca/uhn/hl7v2/protocol/TransportLayer.html#TransportLayer">TransportLayer theTransportA, TransportLayer theTransportB) {
33 myTransportA = theTransportA;
34 myTransportB = theTransportB;
35 }
36
37 /**
38 * @return one of the underlying <code>TransportLayer</code>s.
39 */
40 public TransportLayer getTransportA() {
41 return myTransportA;
42 }
43
44 /**
45 * @return the other underlying <code>TransportLayer</code>.
46 */
47 public TransportLayer getTransportB() {
48 return myTransportB;
49 }
50
51 /**
52 * Connects both <code>TransportLayer</code>s in separate threads,
53 * and returns when both have been connected, or when cancelConnect()
54 * is called.
55 */
56 public void connect() throws TransportException {
57 isConnecting = true;
58 ConnectThread c1 = new ConnectThread(myTransportA);
59 ConnectThread c2 = new ConnectThread(myTransportB);
60 c1.start();
61 c2.start();
62
63 while (isConnecting
64 && (!c1.isConnected() || !c2.isConnected())
65 && c1.getException() == null
66 && c2.getException() == null) {
67
68 try {
69 Thread.sleep(1);
70 } catch (InterruptedException ignored) {}
71 }
72
73 if (c1.getException() != null) throw c1.getException();
74 if (c2.getException() != null) throw c2.getException();
75 }
76
77 public void disconnect() throws TransportException {
78 myTransportA.disconnect();
79 myTransportB.disconnect();
80 }
81
82 /**
83 * Cancels a connect() in progress. Since connect() blocks, this must
84 * be called from a separate thread.
85 */
86 public void cancelConnect() {
87 isConnecting = false;
88 }
89
90 /**
91 * A class to facilitate connecting a <code>TransportLayer</code> in
92 * a separate thread. This is needed when we want to perform two connections
93 * that are initiated remotely, and we don't know the order in which the
94 * remote system will initiate the connections.
95 *
96 * @author <a href="mailto:bryan.tripp@uhn.on.ca">Bryan Tripp</a>
97 * @version $Revision: 1.1 $ updated on $Date: 2007-02-19 02:24:26 $ by $Author: jamesagnew $
98 */
99 private static class ConnectThread extends Thread {
100
101 private final 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 }