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    
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     */
022    public 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    }