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(AcceptorThread.TIMEOUT, TimeUnit.MILLISECONDS); 181 if (newSocket != null) { 182 log.info("Accepted connection from {} on port {}", newSocket.socket.getInetAddress().getHostAddress(), port); 183 Connection c = new Connection(getParser(), getLlp(), newSocket.socket, 184 getExecutorService()); 185 newConnection(c); 186 } 187 } catch (InterruptedException ie) { 188 // just timed out 189 } catch (Exception e) { 190 log.error("Error while accepting connections: ", e); 191 } 192 } 193 194 /** 195 * Close down socket 196 */ 197 @Override 198 protected void afterTermination() { 199 super.afterTermination(); 200 acceptor.stop(); 201 } 202 203 /** 204 * Run server from command line. Port number should be passed as an 205 * argument, and a file containing a list of Applications to use can also be 206 * specified as an optional argument (as per 207 * <code>loadApplicationsFromFile(...)</code>). Uses the default 208 * LowerLayerProtocol. 209 */ 210 public static void main(String args[]) { 211 if (args.length < 1 || args.length > 2) { 212 System.out 213 .println("Usage: ca.uhn.hl7v2.app.SimpleServer port_num [application_spec_file_name]"); 214 System.exit(1); 215 } 216 217 int port = 0; 218 try { 219 port = Integer.parseInt(args[0]); 220 } catch (NumberFormatException e) { 221 System.err.println("The given port (" + args[0] 222 + ") is not an integer."); 223 System.exit(1); 224 } 225 226 File appFile = null; 227 if (args.length == 2) { 228 appFile = new File(args[1]); 229 } 230 231 try { 232 SimpleServer server = new SimpleServer(port); 233 if (appFile != null) 234 server.loadApplicationsFromFile(appFile); 235 server.start(); 236 } catch (Exception e) { 237 e.printStackTrace(); 238 } 239 240 } 241 242}