001/* 002 * Created on 20-May-2004 003 */ 004package ca.uhn.hl7v2.protocol.impl; 005 006import ca.uhn.hl7v2.protocol.TransportException; 007import 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 */ 022public 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}