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