| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| SimpleServer |
|
| 2.2;2.2 |
| 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 "SimpleServer.java". Description: | |
| 10 | * "A simple TCP/IP-based HL7 server." | |
| 11 | * | |
| 12 | * The Initial Developer of the Original Code is University Health Network. Copyright (C) | |
| 13 | * 2002. All Rights Reserved. | |
| 14 | * | |
| 15 | * Contributor(s): Kyle Buza | |
| 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.util.concurrent.BlockingQueue; | |
| 31 | import java.util.concurrent.ExecutorService; | |
| 32 | import java.util.concurrent.LinkedBlockingQueue; | |
| 33 | import java.util.concurrent.TimeUnit; | |
| 34 | ||
| 35 | import org.slf4j.Logger; | |
| 36 | import org.slf4j.LoggerFactory; | |
| 37 | ||
| 38 | import ca.uhn.hl7v2.DefaultHapiContext; | |
| 39 | import ca.uhn.hl7v2.HapiContext; | |
| 40 | import ca.uhn.hl7v2.app.AcceptorThread.AcceptedSocket; | |
| 41 | import ca.uhn.hl7v2.concurrent.DefaultExecutorService; | |
| 42 | import ca.uhn.hl7v2.llp.LowerLayerProtocol; | |
| 43 | import ca.uhn.hl7v2.llp.MinLowerLayerProtocol; | |
| 44 | import ca.uhn.hl7v2.parser.Parser; | |
| 45 | import ca.uhn.hl7v2.parser.PipeParser; | |
| 46 | import ca.uhn.hl7v2.util.SocketFactory; | |
| 47 | ||
| 48 | /** | |
| 49 | * <p> | |
| 50 | * A simple TCP/IP-based HL7 server. This server listens for connections on a | |
| 51 | * particular port, and creates a ConnectionManager for each incoming | |
| 52 | * connection. | |
| 53 | * </p> | |
| 54 | * <p> | |
| 55 | * A single SimpleServer can only service requests that use a single class of | |
| 56 | * LowerLayerProtocol (specified at construction time). | |
| 57 | * </p> | |
| 58 | * <p> | |
| 59 | * The ConnectionManager uses a {@link PipeParser} of the version specified in | |
| 60 | * the constructor | |
| 61 | * </p> | |
| 62 | * <p> | |
| 63 | * ConnectionManagers currently only support original mode processing. | |
| 64 | * </p> | |
| 65 | * <p> | |
| 66 | * The ConnectionManager routes messages to various {@link Application}s based | |
| 67 | * on message type. From the HL7 perspective, an {@link Application} is | |
| 68 | * something that does something with a message. | |
| 69 | * </p> | |
| 70 | * | |
| 71 | * @author Bryan Tripp | |
| 72 | * @author Christian Ohr | |
| 73 | */ | |
| 74 | public class SimpleServer extends HL7Service { | |
| 75 | ||
| 76 | /** | |
| 77 | * Socket timeout for simple server | |
| 78 | */ | |
| 79 | public static final int SO_TIMEOUT = AcceptorThread.TIMEOUT; | |
| 80 | ||
| 81 | 5 | private static final Logger log = LoggerFactory.getLogger(SimpleServer.class); |
| 82 | ||
| 83 | private int port; | |
| 84 | private boolean tls; | |
| 85 | private final BlockingQueue<AcceptedSocket> queue; | |
| 86 | private AcceptorThread acceptor; | |
| 87 | private HapiContext hapiContext; | |
| 88 | ||
| 89 | /** | |
| 90 | * Creates a new instance of SimpleServer that listens on the given port, | |
| 91 | * using the {@link MinLowerLayerProtocol} and a standard {@link PipeParser}. | |
| 92 | */ | |
| 93 | public SimpleServer(int port) { | |
| 94 | 10 | this(port, new MinLowerLayerProtocol(), new PipeParser(), false); |
| 95 | 10 | } |
| 96 | ||
| 97 | /** | |
| 98 | * Creates a new instance of SimpleServer that listens on the given port, | |
| 99 | * using the {@link MinLowerLayerProtocol} and a standard {@link PipeParser}. | |
| 100 | */ | |
| 101 | public SimpleServer(int port, boolean tls) { | |
| 102 | 0 | this(port, new MinLowerLayerProtocol(), new PipeParser(), tls); |
| 103 | 0 | } |
| 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 | 10 | this(port, llp, parser, false); |
| 110 | 10 | } |
| 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 | 20 | this(port, llp, parser, tls, DefaultExecutorService.getDefaultService()); |
| 117 | 20 | } |
| 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 | 20 | super(parser, llp, executorService); |
| 127 | 20 | this.port = port; |
| 128 | 20 | this.tls = tls; |
| 129 | 20 | this.hapiContext = new DefaultHapiContext(); |
| 130 | 20 | this.queue = new LinkedBlockingQueue<AcceptedSocket>(100); |
| 131 | 20 | } |
| 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 | 105 | super(hapiContext); |
| 143 | 105 | this.hapiContext = hapiContext; |
| 144 | 105 | this.port = port; |
| 145 | 105 | this.tls = tls; |
| 146 | 105 | this.queue = new LinkedBlockingQueue<AcceptedSocket>(100); |
| 147 | 105 | } |
| 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 | 120 | super.afterStartup(); |
| 158 | 120 | log.info("Starting SimpleServer running on port {}", port); |
| 159 | 120 | SocketFactory ss = this.hapiContext.getSocketFactory(); |
| 160 | 120 | acceptor = new AcceptorThread(port, tls, getExecutorService(), queue, ss); |
| 161 | 120 | acceptor.start(); |
| 162 | 0 | } catch (Exception e) { |
| 163 | 0 | log.error("Failed starting SimpleServer on port", port); |
| 164 | 0 | throw new RuntimeException(e); |
| 165 | 120 | } |
| 166 | 120 | } |
| 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 | 3337 | if (acceptor.getServiceExitedWithException() != null) { |
| 175 | 5 | setServiceExitedWithException(acceptor.getServiceExitedWithException()); |
| 176 | } | |
| 177 | ||
| 178 | try { | |
| 179 | // Wait some period of time for connections | |
| 180 | 3337 | AcceptedSocket newSocket = queue.poll(500, TimeUnit.MILLISECONDS); |
| 181 | 3262 | if (newSocket != null) { |
| 182 | 370 | log.info("Accepted connection from {}:{} on local port {}", |
| 183 | 185 | new Object[] { newSocket.socket.getInetAddress().getHostAddress(), newSocket.socket.getPort(), port }); |
| 184 | 185 | ActiveConnection c = new ActiveConnection(getParser(), getLlp(), newSocket.socket, |
| 185 | 185 | getExecutorService()); |
| 186 | 185 | newConnection(c); |
| 187 | } | |
| 188 | 45 | } catch (InterruptedException ie) { |
| 189 | // just timed out | |
| 190 | 0 | } catch (Exception e) { |
| 191 | 0 | log.error("Error while accepting connections: ", e); |
| 192 | 3307 | } |
| 193 | 3307 | } |
| 194 | ||
| 195 | /** | |
| 196 | * Close down socket | |
| 197 | */ | |
| 198 | @Override | |
| 199 | protected void afterTermination() { | |
| 200 | 90 | super.afterTermination(); |
| 201 | 90 | acceptor.stop(); |
| 202 | 90 | } |
| 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 | 0 | if (args.length < 1 || args.length > 2) { |
| 213 | 0 | System.out |
| 214 | 0 | .println("Usage: ca.uhn.hl7v2.app.SimpleServer port_num [application_spec_file_name]"); |
| 215 | 0 | System.exit(1); |
| 216 | } | |
| 217 | ||
| 218 | 0 | int port = 0; |
| 219 | try { | |
| 220 | 0 | port = Integer.parseInt(args[0]); |
| 221 | 0 | } catch (NumberFormatException e) { |
| 222 | 0 | System.err.println("The given port (" + args[0] |
| 223 | + ") is not an integer."); | |
| 224 | 0 | System.exit(1); |
| 225 | 0 | } |
| 226 | ||
| 227 | 0 | File appFile = null; |
| 228 | 0 | if (args.length == 2) { |
| 229 | 0 | appFile = new File(args[1]); |
| 230 | } | |
| 231 | ||
| 232 | try { | |
| 233 | 0 | SimpleServer server = new SimpleServer(port); |
| 234 | 0 | if (appFile != null) |
| 235 | 0 | server.loadApplicationsFromFile(appFile); |
| 236 | 0 | server.start(); |
| 237 | 0 | } catch (Exception e) { |
| 238 | 0 | e.printStackTrace(); |
| 239 | 0 | } |
| 240 | ||
| 241 | 0 | } |
| 242 | ||
| 243 | } |