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 | } |