View Javadoc

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  	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  		this(port, new MinLowerLayerProtocol(), new PipeParser(), false);
95  	}
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 		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 }