Coverage Report - ca.uhn.hl7v2.app.TwoPortService
 
Classes in this File Line Coverage Branch Coverage Complexity
TwoPortService
63%
60/94
53%
16/30
2.769
 
 1  
 /**
 2  
 The contents of this file are subject to the Mozilla Public License Version 1.1 
 3  
 (the "License"); you may not use this file except in compliance with the License. 
 4  
 You may obtain a copy of the License at http://www.mozilla.org/MPL/ 
 5  
 Software distributed under the License is distributed on an "AS IS" basis, 
 6  
 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the 
 7  
 specific language governing rights and limitations under the License. 
 8  
 
 9  
 The Original Code is "TwoPortService.java".  Description: 
 10  
 "A TCP/IP-based HL7 Service that uses separate ports for inbound and outbound messages." 
 11  
 
 12  
 The Initial Developer of the Original Code is University Health Network. Copyright (C) 
 13  
 2001.  All Rights Reserved. 
 14  
 
 15  
 Contributor(s): ______________________________________. 
 16  
 
 17  
 Alternatively, the contents of this file may be used under the terms of the 
 18  
 GNU General Public License (the  �GPL�), in which case the provisions of the GPL are 
 19  
 applicable instead of those above.  If you wish to allow use of your version of this 
 20  
 file only under the terms of the GPL and not to allow others to use your version 
 21  
 of this file under the MPL, indicate your decision by deleting  the provisions above 
 22  
 and replace  them with the notice and other provisions required by the GPL License.  
 23  
 If you do not delete the provisions above, a recipient may use your version of 
 24  
 this file under either the MPL or the GPL. 
 25  
  */
 26  
 
 27  
 package ca.uhn.hl7v2.app;
 28  
 
 29  
 import java.io.File;
 30  
 import java.io.IOException;
 31  
 import java.net.Socket;
 32  
 import java.net.SocketException;
 33  
 import java.util.HashMap;
 34  
 import java.util.Map;
 35  
 import java.util.concurrent.BlockingQueue;
 36  
 import java.util.concurrent.ExecutorService;
 37  
 import java.util.concurrent.LinkedBlockingQueue;
 38  
 import java.util.concurrent.TimeUnit;
 39  
 
 40  
 import org.slf4j.Logger;
 41  
 import org.slf4j.LoggerFactory;
 42  
 
 43  
 import ca.uhn.hl7v2.DefaultHapiContext;
 44  
 import ca.uhn.hl7v2.HapiContext;
 45  
 import ca.uhn.hl7v2.app.AcceptorThread.AcceptedSocket;
 46  
 import ca.uhn.hl7v2.concurrent.DefaultExecutorService;
 47  
 import ca.uhn.hl7v2.llp.LLPException;
 48  
 import ca.uhn.hl7v2.llp.LowerLayerProtocol;
 49  
 import ca.uhn.hl7v2.llp.MinLowerLayerProtocol;
 50  
 import ca.uhn.hl7v2.parser.Parser;
 51  
 import ca.uhn.hl7v2.parser.PipeParser;
 52  
 import ca.uhn.hl7v2.util.SocketFactory;
 53  
 
 54  
 /**
 55  
  * A TCP/IP-based HL7 Service that uses separate ports for inbound and outbound
 56  
  * messages. A connection is only activated when the same remote host connects
 57  
  * to both the inbound and outbound ports.
 58  
  * 
 59  
  * @author Bryan Tripp
 60  
  */
 61  
 public class TwoPortService extends HL7Service {
 62  
 
 63  1
         private static final Logger log = LoggerFactory
 64  
                         .getLogger(TwoPortService.class);
 65  
 
 66  4
         private Map<String, AcceptedSocket> waitingForSecondSocket = new HashMap<String, AcceptedSocket>();
 67  
         private int inboundPort;
 68  
         private int outboundPort;
 69  
         private boolean tls;
 70  
         private BlockingQueue<AcceptedSocket> queue;
 71  
         private AcceptorThread inboundAcceptor, outboundAcceptor;
 72  
         private final HapiContext hapiContext;
 73  
 
 74  
         public TwoPortService(int inboundPort, int outboundPort) {
 75  2
                 this(new PipeParser(), new MinLowerLayerProtocol(), inboundPort,
 76  
                                 outboundPort, false);
 77  2
         }
 78  
 
 79  
         public TwoPortService(int inboundPort, int outboundPort, boolean tls) {
 80  0
                 this(new PipeParser(), new MinLowerLayerProtocol(), inboundPort,
 81  
                                 outboundPort, tls);
 82  0
         }
 83  
 
 84  
         /** Creates a new instance of TwoPortService */
 85  
         public TwoPortService(Parser parser, LowerLayerProtocol llp,
 86  
                         int inboundPort, int outboundPort, boolean tls) {
 87  2
                 this(parser, llp, inboundPort, outboundPort, tls,
 88  
                                 DefaultExecutorService.getDefaultService());
 89  2
         }
 90  
 
 91  
         /** Creates a new instance of TwoPortService */
 92  
         public TwoPortService(HapiContext hapiContext, 
 93  
                         int inboundPort, int outboundPort, boolean tls) {
 94  2
                 super(hapiContext);
 95  2
                 this.hapiContext = hapiContext;
 96  2
                 this.queue = new LinkedBlockingQueue<AcceptedSocket>();
 97  2
                 this.inboundPort = inboundPort;
 98  2
                 this.outboundPort = outboundPort;
 99  2
                 this.tls = tls;
 100  
                 
 101  2
                 if (inboundPort == outboundPort) {
 102  0
                         throw new IllegalArgumentException("Inbound port and outbound port can not be the same");
 103  
                 }
 104  2
                 if (inboundPort < 1) {
 105  0
                         throw new IllegalArgumentException("Invalid inbound port");
 106  
                 }
 107  2
                 if (outboundPort < 1) {
 108  0
                         throw new IllegalArgumentException("Invalid outbound port");
 109  
                 }
 110  
                 
 111  2
         }
 112  
 
 113  
         /** Creates a new instance of TwoPortService */
 114  
         public TwoPortService(Parser parser, LowerLayerProtocol llp,
 115  
                         int inboundPort, int outboundPort, boolean tls,
 116  
                         ExecutorService executorService) {
 117  2
                 super(parser, llp, executorService);
 118  2
                 this.hapiContext = new DefaultHapiContext();
 119  2
                 this.queue = new LinkedBlockingQueue<AcceptedSocket>();
 120  2
                 this.inboundPort = inboundPort;
 121  2
                 this.outboundPort = outboundPort;
 122  2
                 this.tls = tls;
 123  2
         }
 124  
 
 125  
         /**
 126  
          * Launches two threads that concurrently listen on the inboundPort and
 127  
          * outboundPort.
 128  
          * 
 129  
          * @see ca.uhn.hl7v2.app.HL7Service#afterStartup()
 130  
          */
 131  
         @Override
 132  
         protected void afterStartup() {
 133  
                 try {
 134  3
                         super.afterStartup();
 135  3
                         inboundAcceptor = createAcceptThread(inboundPort);
 136  3
                         outboundAcceptor = createAcceptThread(outboundPort);
 137  3
                         inboundAcceptor.start();
 138  3
                         outboundAcceptor.start();
 139  3
                         log.info("TwoPortService running on ports {} and {}", inboundPort,
 140  
                                         outboundPort);
 141  0
                 } catch (IOException e) {
 142  0
                         log.error("Could not run TwoPortService on ports {} and {}",
 143  
                                         inboundPort, outboundPort);
 144  0
                         throw new RuntimeException(e);
 145  3
                 }
 146  3
         }
 147  
 
 148  
         /**
 149  
          * Terminate the two acceptor threads
 150  
          * 
 151  
          * @see ca.uhn.hl7v2.app.HL7Service#afterTermination()
 152  
          */
 153  
         @Override
 154  
         protected void afterTermination() {
 155  3
                 super.afterTermination();
 156  3
                 inboundAcceptor.stop();
 157  3
                 outboundAcceptor.stop();
 158  3
         }
 159  
 
 160  
         /**
 161  
          * Polls for accepted sockets
 162  
          */
 163  
         protected void handle() {
 164  27
                 if (inboundAcceptor.getServiceExitedWithException() != null) {
 165  0
                         setServiceExitedWithException(inboundAcceptor.getServiceExitedWithException());
 166  
                 }
 167  27
                 if (outboundAcceptor.getServiceExitedWithException() != null) {
 168  0
                         setServiceExitedWithException(outboundAcceptor.getServiceExitedWithException());
 169  
                 }
 170  
                 
 171  
                 try {
 172  27
                         Connection conn = acceptConnection(queue.poll(2, TimeUnit.SECONDS));
 173  27
                         if (conn != null) {
 174  11
                                 log.info("Accepted connection from "
 175  
                                                 + conn.getRemoteAddress().getHostAddress());
 176  11
                                 newConnection(conn);
 177  
                         }
 178  0
                 } catch (Exception e) {
 179  0
                         log.error("Error while accepting connections: ", e);
 180  27
                 }
 181  27
         }
 182  
 
 183  
         /**
 184  
          * Helper method that checks whether the newSocket completes a two-port
 185  
          * connection or not. If yes, the {@link Connection} object is created and
 186  
          * returned.
 187  
          */
 188  
         private Connection acceptConnection(AcceptedSocket newSocket)
 189  
                         throws LLPException, IOException {
 190  27
                 Connection conn = null;
 191  27
                 if (newSocket != null) {
 192  22
                         String address = newSocket.socket.getInetAddress().getHostAddress();
 193  22
                         AcceptedSocket otherSocket = waitingForSecondSocket.remove(address);
 194  22
                         if (otherSocket != null && otherSocket.origin != newSocket.origin) {
 195  11
                                 log.debug("Socket {} completes a two-port connection",
 196  
                                                 newSocket.socket);
 197  11
                                 Socket in = getInboundSocket(newSocket, otherSocket);
 198  11
                                 Socket out = getOutboundSocket(newSocket, otherSocket);
 199  11
                                 conn = new Connection(getParser(), getLlp(), in, out,
 200  
                                                 getExecutorService());
 201  11
                         } else {
 202  11
                                 log.debug(
 203  
                                                 "Registered {} Still waiting for second socket for two-port connection",
 204  
                                                 newSocket.socket);
 205  11
                                 waitingForSecondSocket.put(address, newSocket);
 206  
                         }
 207  
                 }
 208  27
                 return conn;
 209  
         }
 210  
 
 211  
         private Socket getInboundSocket(AcceptedSocket socket1,
 212  
                         AcceptedSocket socket2) {
 213  11
                 return socket1.origin == inboundAcceptor ? socket1.socket
 214  
                                 : socket2.socket;
 215  
         }
 216  
 
 217  
         private Socket getOutboundSocket(AcceptedSocket socket1,
 218  
                         AcceptedSocket socket2) {
 219  11
                 return socket1.origin == outboundAcceptor ? socket1.socket
 220  
                                 : socket2.socket;
 221  
         }
 222  
 
 223  
         protected AcceptorThread createAcceptThread(int port)
 224  
                         throws SocketException, IOException {
 225  6
                 SocketFactory ss = this.hapiContext.getSocketFactory();
 226  6
                 return new AcceptorThread(port, tls, getExecutorService(), queue, ss);
 227  
         }
 228  
 
 229  
         /**
 230  
          * Run server from command line. Inbound and outbound port numbers should be
 231  
          * provided as arguments, and a file containing a list of Applications to
 232  
          * use can also be specified as an optional argument (as per
 233  
          * <code>super.loadApplicationsFromFile(...)</code>). Uses the default
 234  
          * LowerLayerProtocol.
 235  
          */
 236  
         public static void main(String args[]) {
 237  0
                 if (args.length < 2 || args.length > 3) {
 238  0
                         System.out
 239  
                                         .println("Usage: ca.uhn.hl7v2.app.TwoPortService inbound_port outbound_port [application_spec_file_name]");
 240  0
                         System.exit(1);
 241  
                 }
 242  
 
 243  0
                 int inPort = 0;
 244  0
                 int outPort = 0;
 245  
                 try {
 246  0
                         inPort = Integer.parseInt(args[0]);
 247  0
                         outPort = Integer.parseInt(args[1]);
 248  0
                 } catch (NumberFormatException e) {
 249  0
                         System.err.println("One of the given ports (" + args[0] + " or "
 250  
                                         + args[1] + ") is not an integer.");
 251  0
                         System.exit(1);
 252  0
                 }
 253  
 
 254  0
                 File appFile = null;
 255  0
                 if (args.length == 3) {
 256  0
                         appFile = new File(args[2]);
 257  
                 }
 258  
 
 259  
                 try {
 260  0
                         TwoPortService server = new TwoPortService(inPort, outPort);
 261  0
                         if (appFile != null)
 262  0
                                 server.loadApplicationsFromFile(appFile);
 263  0
                         server.start();
 264  0
                 } catch (Exception e) {
 265  0
                         e.printStackTrace();
 266  0
                 }
 267  
 
 268  0
         }
 269  
 
 270  
 }