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    
027    package ca.uhn.hl7v2.app;
028    
029    import java.io.File;
030    import java.util.concurrent.BlockingQueue;
031    import java.util.concurrent.ExecutorService;
032    import java.util.concurrent.LinkedBlockingQueue;
033    import java.util.concurrent.TimeUnit;
034    
035    import org.slf4j.Logger;
036    import org.slf4j.LoggerFactory;
037    
038    import ca.uhn.hl7v2.app.AcceptorThread.AcceptedSocket;
039    import ca.uhn.hl7v2.concurrent.DefaultExecutorService;
040    import ca.uhn.hl7v2.llp.LowerLayerProtocol;
041    import ca.uhn.hl7v2.llp.MinLowerLayerProtocol;
042    import ca.uhn.hl7v2.parser.Parser;
043    import 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     */
071    public 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    }