001    /**
002    The contents of this file are subject to the Mozilla Public License Version 1.1 
003    (the "License"); you may not use this file except in compliance with the License. 
004    You may obtain a copy of the License at http://www.mozilla.org/MPL/ 
005    Software distributed under the License is distributed on an "AS IS" basis, 
006    WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the 
007    specific language governing rights and limitations under the License. 
008    
009    The Original Code is "Connection.java".  Description: 
010    "A TCP/IP connection to a remote HL7 server." 
011    
012    The Initial Developer of the Original Code is University Health Network. Copyright (C) 
013    2002.  All Rights Reserved. 
014    
015    Contributor(s): ______________________________________. 
016    
017    Alternatively, the contents of this file may be used under the terms of the 
018    GNU General Public License (the  ?GPL?), in which case the provisions of the GPL are 
019    applicable instead of those above.  If you wish to allow use of your version of this 
020    file only under the terms of the GPL and not to allow others to use your version 
021    of this file under the MPL, indicate your decision by deleting  the provisions above 
022    and replace  them with the notice and other provisions required by the GPL License.  
023    If you do not delete the provisions above, a recipient may use your version of 
024    this file under either the MPL or the GPL. 
025    */
026    
027    package ca.uhn.hl7v2.app;
028    
029    import java.io.IOException;
030    import java.net.InetAddress;
031    import java.net.Socket;
032    import java.util.ArrayList;
033    import java.util.HashMap;
034    
035    import ca.uhn.hl7v2.llp.HL7Writer;
036    import ca.uhn.hl7v2.llp.LLPException;
037    import ca.uhn.hl7v2.llp.LowerLayerProtocol;
038    import ca.uhn.hl7v2.parser.Parser;
039    import ca.uhn.log.HapiLog;
040    import ca.uhn.log.HapiLogFactory;
041    
042    /**
043     * A TCP/IP connection to a remote HL7 server.  
044     * @author Bryan Tripp
045     */
046    public class Connection {
047    
048        private static final HapiLog log = HapiLogFactory.getHapiLog(Connection.class);
049    
050        private Initiator initiator;
051        private Responder responder;
052        private ArrayList sockets;
053        private HL7Writer ackWriter;
054        private HL7Writer sendWriter;
055        private Parser parser;
056        private HashMap receipts;
057        private ArrayList receivers;
058        private boolean open = true;
059    
060        /** 
061         * Creates a new instance of Connection, with inbound and outbound 
062         * communication on a single port. 
063         */
064        public Connection(Parser parser, LowerLayerProtocol llp, Socket bidirectional) throws LLPException, IOException {
065            init(parser);
066            ackWriter = llp.getWriter(bidirectional.getOutputStream());
067            sendWriter = ackWriter;
068            sockets.add(bidirectional);
069            Receiver r = new Receiver(this, llp.getReader(bidirectional.getInputStream()));
070            r.start();
071            receivers.add(r);
072            this.initiator = new Initiator(this);
073        }
074    
075        /** 
076         * Creates a new instance of Connection, with inbound communication on one 
077         * port and outbound on another.
078         */
079        public Connection(Parser parser, LowerLayerProtocol llp, Socket inbound, Socket outbound)
080            throws LLPException, IOException {
081            init(parser);
082            ackWriter = llp.getWriter(inbound.getOutputStream());
083            sendWriter = llp.getWriter(outbound.getOutputStream());
084            sockets.add(outbound); //always add outbound first ... see getRemoteAddress()
085            sockets.add(inbound);
086            Receiver inRec = new Receiver(this, llp.getReader(inbound.getInputStream()));
087            Receiver outRec = new Receiver(this, llp.getReader(outbound.getInputStream()));
088            inRec.start();
089            outRec.start();
090            receivers.add(inRec);
091            receivers.add(outRec);
092            this.initiator = new Initiator(this);
093        }
094    
095        /** Common initialization tasks */
096        private void init(Parser parser) throws LLPException {
097            this.parser = parser;
098            sockets = new ArrayList();
099            receipts = new HashMap();
100            receivers = new ArrayList();
101            responder = new Responder(parser);
102        }
103    
104        /** 
105         * Returns the address of the remote host to which this Connection is connected. 
106         * If separate inbound and outbound sockets are used, the address of the outbound
107         * socket is returned (the addresses should normally be the same, but this isn't 
108         * checked).  
109         */
110        public InetAddress getRemoteAddress() {
111            Socket s = (Socket) sockets.get(0);
112            return s.getInetAddress();
113        }
114    
115        /** Returns the Initiator associated with this connection */
116        public Initiator getInitiator() {
117            return this.initiator;
118        }
119    
120        /** Returns the Responder associated with this connection */
121        public Responder getResponder() {
122            return this.responder;
123        }
124    
125        /** Returns the HL7Writer through which unsolicited outbound messages should be sent. */
126        protected HL7Writer getSendWriter() {
127            return this.sendWriter;
128        }
129    
130        /** Returns the HL7Writer through which responses to inbound messages should be sent. */
131        protected HL7Writer getAckWriter() {
132            return this.ackWriter;
133        }
134    
135        public Parser getParser() {
136            return this.parser;
137        }
138    
139        public String toString() {
140            StringBuffer buf = new StringBuffer();
141            buf.append(getRemoteAddress().getHostName());
142            buf.append(":");
143            for (int i = 0; i < sockets.size(); i++) {
144                buf.append(((Socket) sockets.get(i)).getPort());
145                if (i + 1 < sockets.size())
146                    buf.append(",");
147            }
148            return buf.toString();
149        }
150    
151        /** 
152         * Reserves a future incoming message by ack ID.  When the incoming message 
153         * with the given ack ID arrives, the message will be available through 
154         * the returned MessageReceipt.  
155         */
156        protected MessageReceipt reserveResponse(String messageID) {
157            MessageReceipt mr = new MessageReceipt();
158            receipts.put(messageID, mr);
159            return mr;
160        }
161    
162        /** 
163         * Given the ack ID (MSA-2) of a message, returns the corresponding 
164         * MessageReceipt if one exists (ie if reserveResponse has been called for this 
165         * ack ID).  Otherwise (i.e. if no object is waiting for this message), returns null. 
166         * This method can only be called once per ackId (the record is dropped with the 
167         * call).  
168         */
169        protected MessageReceipt findRecipient(String ackID) {
170            //String ID = getParser().getAckID(message);
171            MessageReceipt mr = null;
172            if (ackID != null)
173                mr = (MessageReceipt) receipts.remove(ackID);
174            return mr;
175        }
176    
177        /** Stops running Receiver threads and closes open sockets */
178        public void close() {
179            for (int i = 0; i < receivers.size(); i++) {
180                ((Receiver) receivers.get(i)).stop();
181            }
182            for (int i = 0; i < sockets.size(); i++) {
183                try {
184                    ((Socket) sockets.get(i)).close();
185                }
186                catch (IOException e) {
187                    log.error("Error while stopping threads and closing sockets",e);
188                }
189            }
190            open = false;
191        }
192    
193        /**
194         * {@inheritDoc}
195         */
196        public int hashCode() {
197            int hashCode = super.hashCode();
198            return hashCode;
199        }
200    
201        /** Returns false if the Connection has been closed. */
202        public boolean isOpen() {
203            return open;
204        }
205    
206    }