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