001 package ca.uhn.hl7v2.validation.app; 002 003 import ca.uhn.hl7v2.util.Terser; 004 import ca.uhn.hl7v2.model.*; 005 import ca.uhn.hl7v2.parser.PipeParser; 006 import ca.uhn.hl7v2.HL7Exception; 007 import org.apache.commons.logging.*; 008 import org.apache.log4j.NDC; 009 010 /** 011 * An application intended for testing messages. The intended use is to route a copy 012 * of (selected) messages to a test application, which identifies and acts on problems independently 013 * of the normal error acknowledgement path (for example by notifying an administrator). 014 * This makes the most sense when used within an interface engine, for example if the 015 * sending nor receiving system use HAPI, but it is desired to route messages to HAPI in 016 * parallel so that they can be fully validated. 017 * @author Bryan Tripp 018 */ 019 public abstract class TestApplication implements ca.uhn.hl7v2.app.Application { 020 021 private PipeParser parser; 022 023 /** Creates a new instance of TestApplication */ 024 public TestApplication() { 025 parser = new PipeParser(); 026 } 027 028 /** 029 * Returns true if this Application wishes to accept the message. By returning 030 * true, this Application declares itself the recipient of the message, accepts 031 * responsibility for it, and must be able to respond appropriately to the sending system. 032 */ 033 public abstract boolean canProcess(Message in); 034 035 036 /** 037 * <p>Calls test(Message in), routes resulting exceptions to configured destinations, and 038 * returns an ack (which should not normally be used since the test app is intended 039 * to operate in parallel with system-to-system communication). </p> 040 * <p>Notification routing is performed using log4j, so you need appropriate settings in a log4j 041 * config file (by default, ./log4j.properties). Different types of exceptions 042 * are all given the same severity (ERROR) but they have different loggers, based 043 * on the exception class name. Specifically, the loggers will be named 044 * ca.uhn.hl7v2.validation.error.{exception class name}. For example: 045 * "ca.uhn.hl7v2.validation.error.DataTypeException". Note that this allows default 046 * settings for all validation errors using the logger "ca.uhn.hl7v2.validation.error". 047 * The intent is for different exceptions to result in different actions, e.g. a 048 * ProfileNotHL7CompliantException should probably just be logged or ignored, while a 049 * ProfileNotFollowedException should probably be emailed to an administrator. </p> 050 */ 051 public Message processMessage(Message in) throws HL7Exception { 052 String context = null; 053 try { 054 context = this.parser.encode(in); 055 } catch (HL7Exception e) { 056 context = "message not encodable"; 057 } 058 //update logging context with message text 059 NDC.push(context); 060 061 LogFactory.getLog("ca.uhn.hl7v2.validation.error").info("Testing message"); 062 063 HL7Exception[] problems = test(in); 064 sendNotifications(problems); 065 066 NDC.pop(); 067 068 Message ack = null; 069 try { 070 ack = ca.uhn.hl7v2.app.DefaultApplication.makeACK((Segment) in.get("MSH")); 071 addProblemsToACK(ack, problems); 072 } catch (java.io.IOException e) { 073 throw new HL7Exception(e); 074 } 075 return ack; 076 } 077 078 /** 079 * <p>Send notification of problems to specified destinations (e.g. log file, email). 080 */ 081 private void sendNotifications(HL7Exception[] problems) { 082 for (int i = 0; i < problems.length; i++) { 083 String exName = problems[i].getClass().getName(); 084 String logName = "ca.uhn.hl7v2.validation.error" + exName.substring(exName.lastIndexOf('.')); 085 LogFactory.getLog(logName).error("message validation failure", problems[i]); 086 } 087 } 088 089 private void addProblemsToACK(Message ack, HL7Exception[] problems) throws HL7Exception { 090 Terser t = new Terser(ack); 091 092 if (problems.length > 0) { 093 t.set("MSA-1", "AE"); 094 t.set("MSA-3", "Errors were encountered while testing the message"); 095 } 096 097 Segment err = (Segment) ack.get("ERR"); 098 for (int i = 0; i < problems.length; i++) { 099 // problems[i].populate(err); FIXME: broken! needs database 100 } 101 } 102 103 /** 104 * Tests the message in some way (as defined by implementing class). 105 * @return exceptions that describe any identified problems with the message 106 * @throws HL7Exception if the message can't be tested (not for errors disovered 107 * during testing) 108 */ 109 public abstract HL7Exception[] test(Message in) throws HL7Exception; 110 111 }