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 }