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