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