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