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}