View Javadoc

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 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 e) {}
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 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 }