001/*
002 * Created on 10-May-2004
003 */
004package ca.uhn.hl7v2.protocol.impl;
005
006import java.io.IOException;
007
008import org.slf4j.Logger;
009import org.slf4j.LoggerFactory;
010
011import ca.uhn.hl7v2.HL7Exception;
012import ca.uhn.hl7v2.app.DefaultApplication;
013import ca.uhn.hl7v2.model.Message;
014import ca.uhn.hl7v2.model.Segment;
015import ca.uhn.hl7v2.parser.GenericParser;
016import ca.uhn.hl7v2.parser.Parser;
017import ca.uhn.hl7v2.protocol.AcceptValidator;
018import ca.uhn.hl7v2.protocol.Processor;
019import ca.uhn.hl7v2.protocol.ProcessorContext;
020import ca.uhn.hl7v2.protocol.Transportable;
021import ca.uhn.hl7v2.util.Terser;
022
023/**
024 * Checks whether messages can be accepted and creates appropriate
025 * ACK messages.  
026 * 
027 * @author <a href="mailto:bryan.tripp@uhn.on.ca">Bryan Tripp</a>
028 * @version $Revision: 1.1 $ updated on $Date: 2007-02-19 02:24:26 $ by $Author: jamesagnew $
029 */
030public class AcceptAcknowledger {
031
032    private static final Logger log = LoggerFactory.getLogger(AcceptAcknowledger.class);
033    
034    private static Parser ourParser = new GenericParser();
035    
036    /** 
037     * Validates the given message against our accept validators, attempts to commit
038     * the message to safe storage, and returns an ACK message indicating acceptance 
039     * or rejection at the accept level (see enhanced mode processing rules in HL7 
040     * chapter 2, v2.5).  
041     */
042    public static AcceptACK validate(ProcessorContext theContext, Transportable theMessage) throws HL7Exception {
043        AcceptACK ruling = null;
044        
045        AcceptValidator[] validators = theContext.getValidators();
046        for (int i = 0; i < validators.length && ruling == null; i++) {
047            AcceptValidator.AcceptRuling vr = validators[i].check(theMessage);            
048            if (!vr.isAcceptable()) {
049                String description = (vr.getReasons().length > 0) ? vr.getReasons()[0] : null;
050                Transportable ack = makeAcceptAck(theMessage, vr.getAckCode(), vr.getErrorCode(), description);
051                ruling = new AcceptACK(false, ack);
052            }
053        }
054        
055        if (ruling == null) {
056            try {
057                theContext.getSafeStorage().store(theMessage);
058                Transportable ack = makeAcceptAck(theMessage, Processor.CA, HL7Exception.MESSAGE_ACCEPTED, "");
059                ruling = new AcceptACK(true, ack);
060            } catch (HL7Exception e) {
061                log.error(e.getMessage(), e);
062                int code = HL7Exception.APPLICATION_INTERNAL_ERROR;
063                Transportable ack = makeAcceptAck(theMessage, Processor.CR, code, e.getMessage());
064                ruling = new AcceptACK(false, ack);
065            }
066        }        
067        
068        return ruling;
069    }
070
071
072    private static Transportable makeAcceptAck(Transportable theMessage, String theAckCode, int theErrorCode, String theDescription) throws HL7Exception {
073        
074        Segment header = ourParser.getCriticalResponseData(theMessage.getMessage());
075        Message out;
076        try {
077            out = DefaultApplication.makeACK(header);
078        } catch (IOException e) {
079            throw new HL7Exception(e);
080        }
081        
082        Terser t = new Terser(out);
083        t.set("/MSA-1", theAckCode);
084
085        //TODO: when 2.5 is available, use 2.5 fields for remaining problems 
086        if (theErrorCode != HL7Exception.MESSAGE_ACCEPTED) {
087            t.set("/MSA-3", theDescription.substring(0, Math.min(80, theDescription.length())));            
088            t.set("/ERR-1-4-1", String.valueOf(theErrorCode));
089            t.set("/ERR-1-4-3", "HL70357");
090        }
091        
092        String originalEncoding = ourParser.getEncoding(theMessage.getMessage());
093        String ackText = ourParser.encode(out, originalEncoding);
094        return new TransportableImpl(ackText);
095    }    
096    
097    
098    /**
099     * A structure for decisions as to whether a message can be accepted, 
100     * along with a corresponding accept or reject acknowlegement message. 
101     *  
102     * @author <a href="mailto:bryan.tripp@uhn.on.ca">Bryan Tripp</a>
103     * @version $Revision: 1.1 $ updated on $Date: 2007-02-19 02:24:26 $ by $Author: jamesagnew $
104     */
105    public static class AcceptACK {
106        private Transportable myAck;
107        private boolean myIsAcceptable;
108        
109        public AcceptACK(boolean isAcceptable, Transportable theAck) {
110            myIsAcceptable = isAcceptable;
111            myAck = theAck;
112        }
113        
114        public boolean isAcceptable() {
115            return myIsAcceptable;
116        }
117        
118        public Transportable getMessage() {
119            return myAck;
120        }
121    }
122
123}