001/* 002 * HL7ServerTest.java 003 */ 004 005package ca.uhn.hl7v2.app; 006 007import java.io.BufferedReader; 008import java.io.FileNotFoundException; 009import java.io.IOException; 010import java.io.InputStream; 011import java.io.InputStreamReader; 012import java.io.OutputStream; 013import java.io.PushbackReader; 014import java.io.Reader; 015import java.net.Socket; 016import java.util.ArrayList; 017import java.util.GregorianCalendar; 018import java.util.List; 019import java.util.regex.Matcher; 020import java.util.regex.Pattern; 021 022import org.apache.commons.cli.CommandLine; 023import org.apache.commons.cli.CommandLineParser; 024import org.apache.commons.cli.HelpFormatter; 025import org.apache.commons.cli.Options; 026import org.apache.commons.cli.ParseException; 027import org.apache.commons.cli.PosixParser; 028import org.slf4j.Logger; 029import 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 */ 043public 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}