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    }