001    /*
002     * Created on 10-May-2004
003     */
004    package ca.uhn.hl7v2.protocol.impl;
005    
006    import java.io.IOException;
007    
008    import org.slf4j.Logger;
009    import org.slf4j.LoggerFactory;
010    
011    import ca.uhn.hl7v2.HL7Exception;
012    import ca.uhn.hl7v2.app.DefaultApplication;
013    import ca.uhn.hl7v2.model.Message;
014    import ca.uhn.hl7v2.model.Segment;
015    import ca.uhn.hl7v2.parser.GenericParser;
016    import ca.uhn.hl7v2.parser.Parser;
017    import ca.uhn.hl7v2.protocol.AcceptValidator;
018    import ca.uhn.hl7v2.protocol.Processor;
019    import ca.uhn.hl7v2.protocol.ProcessorContext;
020    import ca.uhn.hl7v2.protocol.Transportable;
021    import 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     */
030    public 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    }