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 "SimpleServer.java". Description: 010 * "A simple TCP/IP-based 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): Kyle Buza 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 027package ca.uhn.hl7v2.app; 028 029import java.io.File; 030import java.util.concurrent.BlockingQueue; 031import java.util.concurrent.ExecutorService; 032import java.util.concurrent.LinkedBlockingQueue; 033import java.util.concurrent.TimeUnit; 034 035import org.slf4j.Logger; 036import org.slf4j.LoggerFactory; 037 038import ca.uhn.hl7v2.app.AcceptorThread.AcceptedSocket; 039import ca.uhn.hl7v2.concurrent.DefaultExecutorService; 040import ca.uhn.hl7v2.llp.LowerLayerProtocol; 041import ca.uhn.hl7v2.llp.MinLowerLayerProtocol; 042import ca.uhn.hl7v2.parser.Parser; 043import ca.uhn.hl7v2.parser.PipeParser; 044 045/** 046 * <p> 047 * A simple TCP/IP-based HL7 server. This server listens for connections on a 048 * particular port, and creates a ConnectionManager for each incoming 049 * connection. 050 * </p> 051 * <p> 052 * A single SimpleServer can only service requests that use a single class of 053 * LowerLayerProtocol (specified at construction time). 054 * </p> 055 * <p> 056 * The ConnectionManager uses a {@link PipeParser} of the version specified in 057 * the constructor 058 * </p> 059 * <p> 060 * ConnectionManagers currently only support original mode processing. 061 * </p> 062 * <p> 063 * The ConnectionManager routes messages to various {@link Application}s based 064 * on message type. From the HL7 perspective, an {@link Application} is 065 * something that does something with a message. 066 * </p> 067 * 068 * @author Bryan Tripp 069 * @author Christian Ohr 070 */ 071public class SimpleServer extends HL7Service { 072 073 /** 074 * Socket timeout for simple server 075 */ 076 public static final int SO_TIMEOUT = AcceptorThread.TIMEOUT; 077 078 private static final Logger log = LoggerFactory 079 .getLogger(SimpleServer.class); 080 private int port; 081 private boolean tls; 082 private final BlockingQueue<AcceptedSocket> queue; 083 private AcceptorThread acceptor; 084 085 /** 086 * Creates a new instance of SimpleServer that listens on the given port, 087 * using the {@link MinLowerLayerProtocol} and a standard {@link PipeParser}. 088 */ 089 public SimpleServer(int port) { 090 this(port, LowerLayerProtocol.makeLLP(), new PipeParser(), false); 091 } 092 093 /** 094 * Creates a new instance of SimpleServer that listens on the given port, 095 * using the {@link MinLowerLayerProtocol} and a standard {@link PipeParser}. 096 */ 097 public SimpleServer(int port, boolean tls) { 098 this(port, LowerLayerProtocol.makeLLP(), new PipeParser(), tls); 099 } 100 101 /** 102 * Creates a new instance of SimpleServer that listens on the given port. 103 */ 104 public SimpleServer(int port, LowerLayerProtocol llp, Parser parser) { 105 this(port, llp, parser, false); 106 } 107 108 /** 109 * Creates a new instance of SimpleServer that listens on the given port. 110 */ 111 public SimpleServer(int port, LowerLayerProtocol llp, Parser parser, boolean tls) { 112 this(port, llp, parser, tls, DefaultExecutorService.getDefaultService()); 113 } 114 115 /** 116 * Creates a new instance of SimpleServer using a custom {link 117 * {@link ExecutorService}. This {@link ExecutorService} instance will 118 * <i>not</i> be shut down after the server stops! 119 */ 120 public SimpleServer(int port, LowerLayerProtocol llp, Parser parser, boolean tls, 121 ExecutorService executorService) { 122 super(parser, llp, executorService); 123 this.port = port; 124 this.tls = tls; 125 queue = new LinkedBlockingQueue<AcceptedSocket>(100); 126 } 127 128 /** 129 * Prepare server by initializing the server socket 130 * 131 * @see ca.uhn.hl7v2.app.HL7Service#afterStartup() 132 */ 133 @Override 134 protected void afterStartup() { 135 try { 136 super.afterStartup(); 137 log.info("Starting SimpleServer running on port {}", port); 138 acceptor = new AcceptorThread(port, tls, getExecutorService(), queue); 139 acceptor.start(); 140 } catch (Exception e) { 141 log.error("Failed starting SimpleServer on port", port); 142 throw new RuntimeException(e); 143 } 144 } 145 146 /** 147 * Loop that waits for a connection and starts a ConnectionManager when it 148 * gets one. 149 */ 150 @Override 151 protected void handle() { 152 if (acceptor.getServiceExitedWithException() != null) { 153 setServiceExitedWithException(acceptor.getServiceExitedWithException()); 154 } 155 156 try { 157 // Wait some period of time for connections 158 AcceptedSocket newSocket = queue.poll(AcceptorThread.TIMEOUT, TimeUnit.MILLISECONDS); 159 if (newSocket != null) { 160 log.info("Accepted connection from {} on port {}", newSocket.socket.getInetAddress().getHostAddress(), port); 161 Connection c = new Connection(parser, llp, newSocket.socket, 162 getExecutorService()); 163 newConnection(c); 164 } 165 } catch (InterruptedException ie) { 166 // just timed out 167 } catch (Exception e) { 168 log.error("Error while accepting connections: ", e); 169 } 170 } 171 172 /** 173 * Close down socket 174 */ 175 @Override 176 protected void afterTermination() { 177 super.afterTermination(); 178 acceptor.stop(); 179 } 180 181 /** 182 * Run server from command line. Port number should be passed as an 183 * argument, and a file containing a list of Applications to use can also be 184 * specified as an optional argument (as per 185 * <code>loadApplicationsFromFile(...)</code>). Uses the default 186 * LowerLayerProtocol. 187 */ 188 public static void main(String args[]) { 189 if (args.length < 1 || args.length > 2) { 190 System.out 191 .println("Usage: ca.uhn.hl7v2.app.SimpleServer port_num [application_spec_file_name]"); 192 System.exit(1); 193 } 194 195 int port = 0; 196 try { 197 port = Integer.parseInt(args[0]); 198 } catch (NumberFormatException e) { 199 System.err.println("The given port (" + args[0] 200 + ") is not an integer."); 201 System.exit(1); 202 } 203 204 File appFile = null; 205 if (args.length == 2) { 206 appFile = new File(args[1]); 207 } 208 209 try { 210 SimpleServer server = new SimpleServer(port); 211 if (appFile != null) 212 server.loadApplicationsFromFile(appFile); 213 server.start(); 214 } catch (Exception e) { 215 e.printStackTrace(); 216 } 217 218 } 219 220}