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.AcknowledgmentCode;
012import ca.uhn.hl7v2.ErrorCode;
013import ca.uhn.hl7v2.HL7Exception;
014import ca.uhn.hl7v2.model.Message;
015import ca.uhn.hl7v2.model.Segment;
016import ca.uhn.hl7v2.parser.GenericParser;
017import ca.uhn.hl7v2.parser.Parser;
018import ca.uhn.hl7v2.protocol.AcceptValidator;
019import ca.uhn.hl7v2.protocol.Processor;
020import ca.uhn.hl7v2.protocol.ProcessorContext;
021import ca.uhn.hl7v2.protocol.Transportable;
022import ca.uhn.hl7v2.util.DeepCopy;
023
024/**
025 * Checks whether messages can be accepted and creates appropriate
026 * ACK messages.  
027 * 
028 * @author <a href="mailto:bryan.tripp@uhn.on.ca">Bryan Tripp</a>
029 * @version $Revision: 1.1 $ updated on $Date: 2007-02-19 02:24:26 $ by $Author: jamesagnew $
030 */
031public class AcceptAcknowledger {
032
033    private static final Logger log = LoggerFactory.getLogger(AcceptAcknowledger.class);
034    
035    private static Parser ourParser = new GenericParser();
036    
037    /** 
038     * Validates the given message against our accept validators, attempts to commit
039     * the message to safe storage, and returns an ACK message indicating acceptance 
040     * or rejection at the accept level (see enhanced mode processing rules in HL7 
041     * chapter 2, v2.5).  
042     */
043    public static AcceptACK validate(ProcessorContext theContext, Transportable theMessage) throws HL7Exception {
044        AcceptACK ruling = null;
045        
046        AcceptValidator[] validators = theContext.getValidators();
047        for (int i = 0; i < validators.length && ruling == null; i++) {
048            AcceptValidator.AcceptRuling vr = validators[i].check(theMessage);            
049            if (!vr.isAcceptable()) {
050                String description = (vr.getReasons().length > 0) ? vr.getReasons()[0] : null;
051                Transportable ack = makeAcceptAck(theMessage, vr.getAckCode(), ErrorCode.errorCodeFor(vr.getErrorCode()), description);
052                ruling = new AcceptACK(false, ack);
053            }
054        }
055        
056        if (ruling == null) {
057            try {
058                theContext.getSafeStorage().store(theMessage);
059                Transportable ack = makeAcceptAck(theMessage, Processor.CA, ErrorCode.MESSAGE_ACCEPTED, "");
060                ruling = new AcceptACK(true, ack);
061            } catch (HL7Exception e) {
062                log.error(e.getMessage(), e);
063                Transportable ack = makeAcceptAck(theMessage, Processor.CR, ErrorCode.APPLICATION_INTERNAL_ERROR, 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, ErrorCode theErrorCode, String theDescription) throws HL7Exception {        
073        Segment header = ourParser.getCriticalResponseData(theMessage.getMessage());
074        Message dummy = header.getMessage();
075        // MSH header refers to dummy, but not the other way round!
076        DeepCopy.copy(header, (Segment)dummy.get("MSH"));
077
078        try {
079            HL7Exception hl7e = new HL7Exception(theDescription, theErrorCode);
080            AcknowledgmentCode code = theAckCode == null ?
081                        AcknowledgmentCode.CR :
082                        AcknowledgmentCode.valueOf(theAckCode);
083                Message out = dummy.generateACK(code, hl7e);
084            String originalEncoding = ourParser.getEncoding(theMessage.getMessage());
085            String ackText = ourParser.encode(out, originalEncoding);
086            return new TransportableImpl(ackText);
087        } catch (IOException e) {
088            throw new HL7Exception(e);
089        }
090        
091    }    
092    
093    
094    /**
095     * A structure for decisions as to whether a message can be accepted, 
096     * along with a corresponding accept or reject acknowlegement message. 
097     *  
098     * @author <a href="mailto:bryan.tripp@uhn.on.ca">Bryan Tripp</a>
099     * @version $Revision: 1.1 $ updated on $Date: 2007-02-19 02:24:26 $ by $Author: jamesagnew $
100     */
101    public static class AcceptACK {
102        private Transportable myAck;
103        private boolean myIsAcceptable;
104        
105        public AcceptACK(boolean isAcceptable, Transportable theAck) {
106            myIsAcceptable = isAcceptable;
107            myAck = theAck;
108        }
109        
110        public boolean isAcceptable() {
111            return myIsAcceptable;
112        }
113        
114        public Transportable getMessage() {
115            return myAck;
116        }
117    }
118
119}