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 = null; 096 try { 097 in = new BufferedReader( 098 new CommentFilterReader( new InputStreamReader( theMsgInputStream ) ) 099 ); 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}