001    /*
002     * HL7ServerTest.java
003     */
004    
005    package ca.uhn.hl7v2.app;
006    
007    import java.io.BufferedReader;
008    import java.io.FileNotFoundException;
009    import java.io.IOException;
010    import java.io.InputStream;
011    import java.io.InputStreamReader;
012    import java.io.OutputStream;
013    import java.io.PushbackReader;
014    import java.io.Reader;
015    import java.net.Socket;
016    import java.util.ArrayList;
017    import java.util.GregorianCalendar;
018    import java.util.List;
019    import java.util.regex.Matcher;
020    import java.util.regex.Pattern;
021    
022    import org.apache.commons.cli.CommandLine;
023    import org.apache.commons.cli.CommandLineParser;
024    import org.apache.commons.cli.HelpFormatter;
025    import org.apache.commons.cli.Options;
026    import org.apache.commons.cli.ParseException;
027    import org.apache.commons.cli.PosixParser;
028    import org.slf4j.Logger;
029    import org.slf4j.LoggerFactory;
030    
031    /**
032     * Helper class used to send messages from a flat file to 
033     * an ip and port. 
034     * 
035     * Messasges are sent using MLLP and ACK protocal
036     * 
037     * @author Laura Bright
038     * @author Neal Acharya
039     * 
040     * @version $Revision: 1.2 $ updated on $Date: 2009-03-18 23:27:58 $ by $Author: jamesagnew $
041     * @deprecated
042     */
043    public class HL7ServerTestHelper {
044        
045        private static final Logger ourLog = LoggerFactory.getLogger(HL7ServerTestHelper.class);
046        
047        private static final String HL7_START_OF_MESSAGE = "\u000b";
048        private static final String HL7_END_OF_MESSGAE = "\u001c";
049    
050        private String host = null;
051        
052        private int port = 0;
053    
054        private Socket socket = null;
055    
056        private OutputStream os = null;
057        private InputStream is = null;
058        
059        public HL7ServerTestHelper(String host, int port) {
060            this.host = host;
061            this.port = port;
062        }
063    
064        /*
065         * 
066         */
067        public void openSocket() throws IOException{
068            socket = new Socket(host, port);
069            socket.setSoLinger(true, 1000);
070            
071            os = socket.getOutputStream();
072            is = socket.getInputStream();
073        }
074        
075        /**
076         * 
077         *
078         */
079        public void closeSocket() {
080            try {
081                Socket sckt = socket;
082                socket = null;
083                if (sckt != null)
084                    sckt.close();
085            }
086            catch (Exception e) {
087                ourLog.error(e.getMessage(), e);
088            }
089        }
090     
091        
092        public int process( InputStream theMsgInputStream ) throws FileNotFoundException, IOException
093        {
094         
095            BufferedReader in =
096                new BufferedReader( 
097                    new CommentFilterReader( new InputStreamReader( theMsgInputStream ) )
098                );
099                    
100            StringBuffer rawMsgBuffer = new StringBuffer();
101            
102            //String line = in.readLine();
103            int c = 0;
104                    while( (c = in.read()) >= 0) {
105                            rawMsgBuffer.append( (char) c);
106                    }
107                    
108                    String[] messages = getHL7Messages(rawMsgBuffer.toString());
109            int retVal = 0;
110            
111            //start time
112            long startTime = new GregorianCalendar().getTimeInMillis(); 
113                
114                
115                    for (int i = 0; i < messages.length; i++) {
116                            sendMessage(messages[i]);       
117                            readAck();      
118                retVal++;
119                    }
120            
121            //end time
122            long endTime =  new GregorianCalendar().getTimeInMillis();
123            
124            //elapsed time
125            long elapsedTime = (endTime - startTime) / 1000;
126            
127            ourLog.info("{} messages sent.", retVal);
128            ourLog.info("Elapsed Time in seconds: {} ", elapsedTime);
129            return retVal;
130                            
131                /*line = line.trim();
132                
133                if ( line.length()!=0 ) {
134                    rawMsgBuffer.append( line );
135                    rawMsgBuffer.append( HL7_SEGMENT_SEPARATOR );
136                }
137                else {
138                    if (rawMsgBuffer.length()!=0) {
139                        String rawMsg = rawMsgBuffer.toString();
140                        sendMessage( rawMsg );
141                        //clear buffer 
142                        rawMsgBuffer = new StringBuffer(); 
143                        //do not wait for ACK, we just want to feed the Hl7Server
144                        
145                        //TODO look into this, the HL7Server should perform better. JMS integration should fix this.
146                        
147                        try {
148                            //wait a sec, give some time to the HL7Server
149                            Thread.sleep(1000); //1 seconds
150                        }
151                        catch (InterruptedException e) {
152                        }
153                    }
154                }
155                                        
156                line = in.readLine();    
157            }*/
158            
159        }
160        
161            private String readAck() throws IOException
162            {
163                    StringBuffer stringbuffer = new StringBuffer();
164                    int i = 0;
165                    do {
166                            i = is.read();
167                            if (i == -1)
168                                    return null;
169                
170                            stringbuffer.append((char) i);
171                    }
172                    while (i != 28);        
173                    return stringbuffer.toString();
174            }
175        
176        
177        
178            /** 
179             * Given a string that contains HL7 messages, and possibly other junk, 
180             * returns an array of the HL7 messages.  
181             * An attempt is made to recognize segments even if there is other 
182             * content between segments, for example if a log file logs segments 
183             * individually with timestamps between them.  
184             * 
185             * @param theSource a string containing HL7 messages 
186             * @return the HL7 messages contained in theSource
187             */
188            public static String[] getHL7Messages(String theSource) {
189                    List<String> messages = new ArrayList<String>(20); 
190                    Pattern startPattern = Pattern.compile("^MSH", Pattern.MULTILINE);
191                    Matcher startMatcher = startPattern.matcher(theSource);
192    
193                    while (startMatcher.find()) {
194                            String messageExtent = 
195                                    getMessageExtent(theSource.substring(startMatcher.start()), startPattern);
196                            
197                            char fieldDelim = messageExtent.charAt(3);
198                            Pattern segmentPattern = Pattern.compile("^[A-Z\\d]{3}\\" + fieldDelim + ".*$", Pattern.MULTILINE);
199                            Matcher segmentMatcher = segmentPattern.matcher(messageExtent);
200                            StringBuffer msg = new StringBuffer();
201                            while (segmentMatcher.find()) {
202                                    msg.append(segmentMatcher.group().trim());
203                                    msg.append('\r');
204                            }
205                            messages.add(msg.toString());
206                    }
207                    return messages.toArray(new String[0]);
208            }
209        
210            /** 
211             * Given a string that contains at least one HL7 message, returns the 
212             * smallest string that contains the first of these messages.  
213             */
214            private static String getMessageExtent(String theSource, Pattern theStartPattern) {
215                    Matcher startMatcher = theStartPattern.matcher(theSource);
216                    if (!startMatcher.find()) {
217                            throw new IllegalArgumentException(theSource + "does not contain message start pattern" 
218                                    + theStartPattern.toString());
219                    }
220            
221                    int start = startMatcher.start();
222                    int end = theSource.length();
223                    if (startMatcher.find()) {
224                            end = startMatcher.start();
225                    }
226            
227                    return theSource.substring(start, end).trim();
228            }
229        
230        
231        private void sendMessage(String theMessage) throws IOException
232        {
233            os.write( HL7_START_OF_MESSAGE.getBytes() );
234            os.write( theMessage.getBytes() );
235            os.write( HL7_END_OF_MESSGAE.getBytes() );
236            os.write(13);
237            os.flush();
238            ourLog.info("Sent: " + theMessage);
239        }
240         
241        
242        
243        /**
244         * Main method for running the application
245         * 
246         * example command lines args:
247         * 
248         * -f UHN_PRO_DEV_PATIENTS.dat -h 142.224.178.152 -p 3999
249         * 
250         */
251        public static void main( String[] theArgs ) {
252    
253            //parse command line arguments        
254    
255            //create the command line parser
256            CommandLineParser parser = new PosixParser();
257    
258            //create the Options
259            Options options = new Options();
260    
261            options.addOption("h", "host", true, "IP of host to send to");
262            options.addOption("p", "port", true, "port to send to");
263            options.addOption("f", "file", true, "file to read HL7 messages from");
264            
265            CommandLine cmdLine = null;
266            try
267            {
268                // parse the command line arguments
269                cmdLine = parser.parse(options, theArgs);
270            }
271            catch (ParseException e)
272            {
273                ourLog.error(e.getMessage(), e);
274                return;
275            }
276    
277            String portString = cmdLine.getOptionValue("p");
278            int port = 0;
279            String host = cmdLine.getOptionValue("h");        
280            String file = cmdLine.getOptionValue("f");
281            
282            if (portString == null || host == null || file == null)
283            {
284                //automatically generate the help statement
285                HelpFormatter formatter = new HelpFormatter();
286                //assuming that a shell script named serverTest will be created
287                formatter.printHelp( "serverTest", options );
288                return;
289            }
290            else {
291                //parse portAsString
292                port = Integer.parseInt(portString);
293            }
294            
295            HL7ServerTestHelper serverTest = new HL7ServerTestHelper( host, port );
296            
297            //InputStream msgInputStream = HL7ServerTestHelper.class.getResourceAsStream( file );
298                    InputStream msgInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(file);          
299            try{            
300                serverTest.openSocket();
301                serverTest.process( msgInputStream );
302            }
303            catch(Exception e){
304                    e.printStackTrace();
305                HelpFormatter formatter = new HelpFormatter();
306                //assuming that a shell script named hl7mom will be created
307                formatter.printHelp( "serverTest", options );
308                System.exit(-1);
309            }
310            
311            serverTest.closeSocket();
312        }
313        
314            /**
315             * TODO: this code is copied from HAPI ... should make it part of HAPI public API instead
316             * Removes C and C++ style comments from a reader stream.  C style comments are
317             * distinguished from URL protocol delimiters by the preceding colon in the
318             * latter.
319             */
320            public static class CommentFilterReader extends PushbackReader {
321            
322                    private final char[] startCPPComment = {'/', '*'};
323                    private final char[] endCPPComment = {'*', '/'};
324                    private final char[] startCComment = {'/', '/'};
325                    private final char[] endCComment = {'\n'};
326                    private final char[] protocolDelim = {':', '/', '/'};
327            
328                    public CommentFilterReader(Reader in) {
329                            super(in, 5);
330                    }
331            
332                    /**
333                     * Returns the next character, not including comments.
334                     */
335                    public int read() throws IOException {
336                            if (atSequence(protocolDelim)) {
337                                    //proceed normally
338                            } else if (atSequence(startCPPComment)) {
339                                    //skip() doesn't seem to work for some reason
340                                    while (!atSequence(endCPPComment)) super.read();
341                                    for (int i = 0; i < endCPPComment.length; i++) super.read();
342                            } else if (atSequence(startCComment)) {
343                                    while (!atSequence(endCComment)) super.read();
344                                    for (int i = 0; i < endCComment.length; i++) super.read();
345                            }
346                            int ret = super.read();
347                            if (ret == 65535) ret = -1;
348                            return ret;            
349                    }
350                    
351                    public int read(char[] cbuf, int off, int len) throws IOException {
352                            int i = -1;
353                            boolean done = false;
354                            while (++i < len) {
355                                    int next = read();
356                                    if (next == 65535 || next == -1) { //Pushback causes -1 to convert to 65535
357                                            done = true;
358                                            break;  
359                                    }
360                                    cbuf[off + i] = (char) next;
361                            }
362                            if (i == 0 && done) i = -1; 
363                            return i; 
364                    }            
365            
366                    /**
367                     * Tests incoming data for match with char sequence, resets reader when done.
368                     */
369                    private boolean atSequence(char[] sequence) throws IOException {
370                            boolean result = true;
371                            int i = -1;
372                            int[] data = new int[sequence.length];
373                            while (++i < sequence.length && result == true) {
374                                    data[i] = super.read();
375                                    if ((char) data[i] != sequence[i]) result = false; //includes case where end of stream reached
376                            }
377                            for (int j = i-1; j >= 0; j--) {
378                                    this.unread(data[j]);
379                            }
380                            return result;
381                    }        
382            }
383        
384    
385    }