View Javadoc

1   /*
2    * HL7ServerTest.java
3    */
4   
5   package ca.uhn.hl7v2.app;
6   
7   import java.io.BufferedReader;
8   import java.io.FileNotFoundException;
9   import java.io.IOException;
10  import java.io.InputStream;
11  import java.io.InputStreamReader;
12  import java.io.OutputStream;
13  import java.io.PushbackReader;
14  import java.io.Reader;
15  import java.net.Socket;
16  import java.util.ArrayList;
17  import java.util.GregorianCalendar;
18  import java.util.List;
19  import java.util.regex.Matcher;
20  import java.util.regex.Pattern;
21  
22  import org.apache.commons.cli.CommandLine;
23  import org.apache.commons.cli.CommandLineParser;
24  import org.apache.commons.cli.HelpFormatter;
25  import org.apache.commons.cli.Options;
26  import org.apache.commons.cli.ParseException;
27  import org.apache.commons.cli.PosixParser;
28  import org.slf4j.Logger;
29  import org.slf4j.LoggerFactory;
30  
31  /**
32   * Helper class used to send messages from a flat file to 
33   * an ip and port. 
34   * 
35   * Messasges are sent using MLLP and ACK protocal
36   * 
37   * @author Laura Bright
38   * @author Neal Acharya
39   * 
40   * @version $Revision: 1.2 $ updated on $Date: 2009-03-18 23:27:58 $ by $Author: jamesagnew $
41   * @deprecated
42   */
43  public class HL7ServerTestHelper {
44      
45      private static final Logger ourLog = LoggerFactory.getLogger(HL7ServerTestHelper.class);
46      
47      private static final String HL7_START_OF_MESSAGE = "\u000b";
48      private static final String HL7_END_OF_MESSGAE = "\u001c";
49  
50      private String host = null;
51      
52      private int port = 0;
53  
54      private Socket socket = null;
55  
56      private OutputStream os = null;
57      private InputStream is = null;
58      
59      public HL7ServerTestHelper(String host, int port) {
60          this.host = host;
61          this.port = port;
62      }
63  
64      /*
65       * 
66       */
67      public void openSocket() throws IOException{
68          socket = new Socket(host, port);
69          socket.setSoLinger(true, 1000);
70          
71          os = socket.getOutputStream();
72          is = socket.getInputStream();
73      }
74      
75      /**
76       * 
77       *
78       */
79      public void closeSocket() {
80          try {
81              Socket sckt = socket;
82              socket = null;
83              if (sckt != null)
84                  sckt.close();
85          }
86          catch (Exception e) {
87              ourLog.error(e.getMessage(), e);
88          }
89      }
90   
91      
92      public int process( InputStream theMsgInputStream ) throws FileNotFoundException, IOException
93      {
94       
95      	BufferedReader in = null;
96      	try {
97  	        in = new BufferedReader( 
98  	                new CommentFilterReader( new InputStreamReader( theMsgInputStream ) )
99  	            );
100 	        	
101 	        StringBuffer rawMsgBuffer = new StringBuffer();
102 	        
103 	        //String line = in.readLine();
104 	        int c = 0;
105 			while( (c = in.read()) >= 0) {
106 				rawMsgBuffer.append( (char) c);
107 			}
108 			
109 			String[] messages = getHL7Messages(rawMsgBuffer.toString());
110 	        int retVal = 0;
111 	        
112 	        //start time
113 	        long startTime = new GregorianCalendar().getTimeInMillis(); 
114 	            
115 	            
116 			for (int i = 0; i < messages.length; i++) {
117 				sendMessage(messages[i]);	
118 				readAck();	
119 	            retVal++;
120 			}
121 	        
122 	        //end time
123 	        long endTime =  new GregorianCalendar().getTimeInMillis();
124 	        
125 	        //elapsed time
126 	        long elapsedTime = (endTime - startTime) / 1000;
127 	        
128 	        ourLog.info("{} messages sent.", retVal);
129 	        ourLog.info("Elapsed Time in seconds: {} ", elapsedTime);
130 	        return retVal;
131     	} finally {
132     		if (in != null) {
133     			try {
134 					in.close();
135 				} catch (IOException e) {
136 				}
137     		}
138     	}
139 			
140         
141     }
142     
143 	private String readAck() throws IOException
144 	{
145 		StringBuffer stringbuffer = new StringBuffer();
146 		int i = 0;
147 		do {
148 			i = is.read();
149 			if (i == -1)
150 				return null;
151             
152 			stringbuffer.append((char) i);
153 		}
154 		while (i != 28);        
155 		return stringbuffer.toString();
156 	}
157     
158     
159     
160 	/** 
161 	 * Given a string that contains HL7 messages, and possibly other junk, 
162 	 * returns an array of the HL7 messages.  
163 	 * An attempt is made to recognize segments even if there is other 
164 	 * content between segments, for example if a log file logs segments 
165 	 * individually with timestamps between them.  
166 	 * 
167 	 * @param theSource a string containing HL7 messages 
168 	 * @return the HL7 messages contained in theSource
169 	 */
170 	public static String[] getHL7Messages(String theSource) {
171 		List<String> messages = new ArrayList<String>(20); 
172 		Pattern startPattern = Pattern.compile("^MSH", Pattern.MULTILINE);
173 		Matcher startMatcher = startPattern.matcher(theSource);
174 
175 		while (startMatcher.find()) {
176 			String messageExtent = 
177 				getMessageExtent(theSource.substring(startMatcher.start()), startPattern);
178 			
179 			char fieldDelim = messageExtent.charAt(3);
180 			Pattern segmentPattern = Pattern.compile("^[A-Z\\d]{3}\\" + fieldDelim + ".*$", Pattern.MULTILINE);
181 			Matcher segmentMatcher = segmentPattern.matcher(messageExtent);
182 			StringBuffer msg = new StringBuffer();
183 			while (segmentMatcher.find()) {
184 				msg.append(segmentMatcher.group().trim());
185 				msg.append('\r');
186 			}
187 			messages.add(msg.toString());
188 		}
189 		return messages.toArray(new String[0]);
190 	}
191     
192 	/** 
193 	 * Given a string that contains at least one HL7 message, returns the 
194 	 * smallest string that contains the first of these messages.  
195 	 */
196 	private static String getMessageExtent(String theSource, Pattern theStartPattern) {
197 		Matcher startMatcher = theStartPattern.matcher(theSource);
198 		if (!startMatcher.find()) {
199 			throw new IllegalArgumentException(theSource + "does not contain message start pattern" 
200 				+ theStartPattern.toString());
201 		}
202     	
203 		int start = startMatcher.start();
204 		int end = theSource.length();
205 		if (startMatcher.find()) {
206 			end = startMatcher.start();
207 		}
208  	
209 		return theSource.substring(start, end).trim();
210 	}
211     
212     
213     private void sendMessage(String theMessage) throws IOException
214     {
215         os.write( HL7_START_OF_MESSAGE.getBytes() );
216         os.write( theMessage.getBytes() );
217         os.write( HL7_END_OF_MESSGAE.getBytes() );
218         os.write(13);
219         os.flush();
220         ourLog.info("Sent: " + theMessage);
221     }
222      
223     
224     
225     /**
226      * Main method for running the application
227      * 
228      * example command lines args:
229      * 
230      * -f UHN_PRO_DEV_PATIENTS.dat -h 142.224.178.152 -p 3999
231      * 
232      */
233     public static void main( String[] theArgs ) {
234 
235         //parse command line arguments        
236 
237         //create the command line parser
238         CommandLineParser parser = new PosixParser();
239 
240         //create the Options
241         Options options = new Options();
242 
243         options.addOption("h", "host", true, "IP of host to send to");
244         options.addOption("p", "port", true, "port to send to");
245         options.addOption("f", "file", true, "file to read HL7 messages from");
246         
247         CommandLine cmdLine = null;
248         try
249         {
250             // parse the command line arguments
251             cmdLine = parser.parse(options, theArgs);
252         }
253         catch (ParseException e)
254         {
255             ourLog.error(e.getMessage(), e);
256             return;
257         }
258 
259         String portString = cmdLine.getOptionValue("p");
260         int port = 0;
261         String host = cmdLine.getOptionValue("h");        
262         String file = cmdLine.getOptionValue("f");
263         
264         if (portString == null || host == null || file == null)
265         {
266             //automatically generate the help statement
267             HelpFormatter formatter = new HelpFormatter();
268             //assuming that a shell script named serverTest will be created
269             formatter.printHelp( "serverTest", options );
270             return;
271         }
272         else {
273             //parse portAsString
274             port = Integer.parseInt(portString);
275         }
276         
277         HL7ServerTestHelper serverTest = new HL7ServerTestHelper( host, port );
278         
279         //InputStream msgInputStream = HL7ServerTestHelper.class.getResourceAsStream( file );
280 		InputStream msgInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(file);		
281         try{            
282             serverTest.openSocket();
283             serverTest.process( msgInputStream );
284         }
285         catch(Exception e){
286         	e.printStackTrace();
287             HelpFormatter formatter = new HelpFormatter();
288             //assuming that a shell script named hl7mom will be created
289             formatter.printHelp( "serverTest", options );
290             System.exit(-1);
291         }
292         
293         serverTest.closeSocket();
294     }
295     
296 	/**
297 	 * TODO: this code is copied from HAPI ... should make it part of HAPI public API instead
298 	 * Removes C and C++ style comments from a reader stream.  C style comments are
299 	 * distinguished from URL protocol delimiters by the preceding colon in the
300 	 * latter.
301 	 */
302 	public static class CommentFilterReader extends PushbackReader {
303         
304 		private final char[] startCPPComment = {'/', '*'};
305 		private final char[] endCPPComment = {'*', '/'};
306 		private final char[] startCComment = {'/', '/'};
307 		private final char[] endCComment = {'\n'};
308 		private final char[] protocolDelim = {':', '/', '/'};
309         
310 		public CommentFilterReader(Reader in) {
311 			super(in, 5);
312 		}
313         
314 		/**
315 		 * Returns the next character, not including comments.
316 		 */
317 		public int read() throws IOException {
318 			if (atSequence(protocolDelim)) {
319 				//proceed normally
320 			} else if (atSequence(startCPPComment)) {
321 				//skip() doesn't seem to work for some reason
322 				while (!atSequence(endCPPComment)) super.read();
323 				for (int i = 0; i < endCPPComment.length; i++) super.read();
324 			} else if (atSequence(startCComment)) {
325 				while (!atSequence(endCComment)) super.read();
326 				for (int i = 0; i < endCComment.length; i++) super.read();
327 			}
328 			int ret = super.read();
329 			if (ret == 65535) ret = -1;
330 			return ret;            
331 		}
332                 
333 		public int read(char[] cbuf, int off, int len) throws IOException {
334 			int i = -1;
335 			boolean done = false;
336 			while (++i < len) {
337 				int next = read();
338 				if (next == 65535 || next == -1) { //Pushback causes -1 to convert to 65535
339 					done = true;
340 					break;  
341 				}
342 				cbuf[off + i] = (char) next;
343 			}
344 			if (i == 0 && done) i = -1; 
345 			return i; 
346 		}            
347         
348 		/**
349 		 * Tests incoming data for match with char sequence, resets reader when done.
350 		 */
351 		private boolean atSequence(char[] sequence) throws IOException {
352 			boolean result = true;
353 			int i = -1;
354 			int[] data = new int[sequence.length];
355 			while (++i < sequence.length && result == true) {
356 				data[i] = super.read();
357 				if ((char) data[i] != sequence[i]) result = false; //includes case where end of stream reached
358 			}
359 			for (int j = i-1; j >= 0; j--) {
360 				this.unread(data[j]);
361 			}
362 			return result;
363 		}        
364 	}
365     
366 
367 }