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