001/**
002The contents of this file are subject to the Mozilla Public License Version 1.1 
003(the "License"); you may not use this file except in compliance with the License. 
004You may obtain a copy of the License at http://www.mozilla.org/MPL/ 
005Software distributed under the License is distributed on an "AS IS" basis, 
006WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the 
007specific language governing rights and limitations under the License. 
008
009The Original Code is "DefaultApplication.java".  Description: 
010"An Application that does nothing with the message and returns an Application 
011 Reject message in response." 
012
013The Initial Developer of the Original Code is University Health Network. Copyright (C) 
0142002.  All Rights Reserved. 
015
016Contributor(s): ______________________________________. 
017
018Alternatively, the contents of this file may be used under the terms of the 
019GNU General Public License (the  "GPL"), in which case the provisions of the GPL are 
020applicable instead of those above.  If you wish to allow use of your version of this 
021file only under the terms of the GPL and not to allow others to use your version 
022of this file under the MPL, indicate your decision by deleting  the provisions above 
023and replace  them with the notice and other provisions required by the GPL License.  
024If you do not delete the provisions above, a recipient may use your version of 
025this file under either the MPL or the GPL. 
026 */
027
028package ca.uhn.hl7v2.app;
029
030import java.io.IOException;
031import java.util.Date;
032import java.util.GregorianCalendar;
033
034import ca.uhn.hl7v2.HL7Exception;
035import ca.uhn.hl7v2.model.DataTypeException;
036import ca.uhn.hl7v2.model.Message;
037import ca.uhn.hl7v2.model.Segment;
038import ca.uhn.hl7v2.model.Structure;
039import ca.uhn.hl7v2.model.primitive.CommonTS;
040import ca.uhn.hl7v2.parser.DefaultModelClassFactory;
041import ca.uhn.hl7v2.parser.ModelClassFactory;
042import ca.uhn.hl7v2.parser.Parser;
043import ca.uhn.hl7v2.util.MessageIDGenerator;
044import ca.uhn.hl7v2.util.Terser;
045
046/**
047 * An Application that does nothing with the message and returns an Application
048 * Reject message in response. To be used when there are no other Applications
049 * that can process a given message.
050 * 
051 * @author Bryan Tripp
052 */
053public class DefaultApplication implements Application {
054
055        /** Creates a new instance of DefaultApplication */
056        public DefaultApplication() {
057        }
058
059        /**
060         * Returns true.
061         */
062        public boolean canProcess(Message in) {
063                return true;
064        }
065
066        /**
067         * Creates and returns an acknowledgement -- the details are determined by
068         * fillDetails().
069         */
070        public Message processMessage(Message in) throws ApplicationException {
071                try {
072                        // get default ACK
073                        Message out = makeACK(in);
074                        fillDetails(out);
075                        return out;
076                } catch (Exception e) {
077                        throw new ApplicationException("Couldn't create response message: "
078                                        + e.getMessage());
079                }
080
081        }
082
083        /**
084         * Fills in the details of an Application Reject message, including response
085         * and error codes, and a text error message. This is the method to override
086         * if you want to respond differently.
087         */
088        public void fillDetails(Message ack) throws ApplicationException {
089                try {
090                        // populate MSA and ERR with generic error ...
091                        Segment msa = (Segment) ack.get("MSA");
092                        Terser.set(msa, 1, 0, 1, 1, "AR");
093                        Terser.set(
094                                        msa,
095                                        3,
096                                        0,
097                                        1,
098                                        1,
099                                        "No appropriate destination could be found to which this message could be routed.");
100                        // this is max length
101
102                        // populate ERR segment if it exists (may not depending on version)
103                        Structure s = ack.get("ERR");
104                        if (s != null) {
105                                Segment err = (Segment) s;
106                                Terser.set(err, 1, 0, 4, 1, "207");
107                                Terser.set(err, 1, 0, 4, 2, "Application Internal Error");
108                                Terser.set(err, 1, 0, 4, 3, "HL70357");
109                        }
110
111                } catch (Exception e) {
112                        throw new ApplicationException(
113                                        "Error trying to create Application Reject message: "
114                                                        + e.getMessage());
115                }
116        }
117
118        /**
119         * Creates an ACK message with the minimum required information from an
120         * inbound message. Optional fields can be filled in afterwards, before the
121         * message is returned. Please note that MSH-10, the outbound message
122         * control ID, is also set using the class
123         * <code>ca.uhn.hl7v2.util.MessageIDGenerator</code>. Also note that the ACK
124         * messages returned is the same version as the version stated in the
125         * inbound MSH if there is a generic ACK for that version, otherwise a
126         * version 2.4 ACK is returned. MSA-1 is set to AA by default.
127         * 
128         * @param inboundHeader
129         *            the MSH segment if the inbound message
130         * @throws IOException
131         *             if there is a problem reading or writing the message ID file
132         * @throws DataTypeException
133         *             if there is a problem setting ACK values
134         */
135        public static Message makeACK(Message message) throws HL7Exception, IOException {
136                return makeACK((Segment)message.get("MSH"));
137        }
138        
139        public static Message makeACK(Segment inboundHeader) throws HL7Exception,
140                        IOException {
141                if (!inboundHeader.getName().equals("MSH"))
142                        throw new HL7Exception(
143                                        "Need an MSH segment to create a response ACK (got "
144                                                        + inboundHeader.getName() + ")");
145
146                // make ACK of correct version
147                Class<? extends Message> clazz = null;
148                try {
149                        Message inbound = inboundHeader.getMessage();
150                        Parser p = inbound.getParser();
151                        ModelClassFactory mcf = p != null ? p.getFactory() : new DefaultModelClassFactory();
152                        String version = inbound.getVersion();
153                        if (version == null)
154                                version = "2.4"; // TODO: This should be set dynamically based on available HL7 version
155                        clazz = mcf.getMessageClass("ACK", version, false);
156                        Message out = clazz.newInstance();
157                        Terser terser = new Terser(out);
158
159                        // populate outbound MSH using data from inbound message ...
160                        Segment outHeader = (Segment) out.get("MSH");
161                        fillResponseHeader(inboundHeader, outHeader);
162
163                        terser.set("/MSH-9-1", "ACK");
164                        terser.set("/MSH-9-2", Terser.get(inboundHeader, 9, 0, 2, 1));
165                        terser.set("/MSH-12", Terser.get(inboundHeader, 12, 0, 1, 1));
166                        terser.set("/MSA-1", "AA");
167                        terser.set("/MSA-2", Terser.get(inboundHeader, 10, 0, 1, 1));
168                        return out;
169
170                } catch (Exception e) {
171                        throw new HL7Exception("Can't instantiate ACK", e);
172                }
173
174        }
175
176        /**
177         * Populates certain required fields in a response message header, using
178         * information from the corresponding inbound message. The current time is
179         * used for the message time field, and <code>MessageIDGenerator</code> is
180         * used to create a unique message ID. Version and message type fields are
181         * not populated.
182         */
183        public static void fillResponseHeader(Segment inbound, Segment outbound)
184                        throws HL7Exception, IOException {
185                if (!inbound.getName().equals("MSH")
186                                || !outbound.getName().equals("MSH"))
187                        throw new HL7Exception("Need MSH segments.  Got "
188                                        + inbound.getName() + " and " + outbound.getName());
189
190                // get MSH data from incoming message ...
191                String encChars = Terser.get(inbound, 2, 0, 1, 1);
192                String fieldSep = Terser.get(inbound, 1, 0, 1, 1);
193                String procID = Terser.get(inbound, 11, 0, 1, 1);
194
195                // populate outbound MSH using data from inbound message ...
196                Terser.set(outbound, 2, 0, 1, 1, encChars);
197                Terser.set(outbound, 1, 0, 1, 1, fieldSep);
198                GregorianCalendar now = new GregorianCalendar();
199                now.setTime(new Date());
200                Terser.set(outbound, 7, 0, 1, 1, CommonTS.toHl7TSFormat(now));
201                Terser.set(outbound, 10, 0, 1, 1, MessageIDGenerator.getInstance()
202                                .getNewID());
203                Terser.set(outbound, 11, 0, 1, 1, procID);
204
205                // revert sender and receiver
206                Terser.set(outbound, 3, 0, 1, 1, Terser.get(inbound, 5, 0, 1, 1));
207                Terser.set(outbound, 4, 0, 1, 1, Terser.get(inbound, 6, 0, 1, 1));
208                Terser.set(outbound, 5, 0, 1, 1, Terser.get(inbound, 3, 0, 1, 1));
209                Terser.set(outbound, 6, 0, 1, 1, Terser.get(inbound, 4, 0, 1, 1));
210        }
211
212}