001    /**
002    The 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. 
004    You may obtain a copy of the License at http://www.mozilla.org/MPL/ 
005    Software distributed under the License is distributed on an "AS IS" basis, 
006    WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the 
007    specific language governing rights and limitations under the License. 
008    
009    The 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    
013    The Initial Developer of the Original Code is University Health Network. Copyright (C) 
014    2002.  All Rights Reserved. 
015    
016    Contributor(s): ______________________________________. 
017    
018    Alternatively, the contents of this file may be used under the terms of the 
019    GNU General Public License (the  "GPL"), in which case the provisions of the GPL are 
020    applicable instead of those above.  If you wish to allow use of your version of this 
021    file only under the terms of the GPL and not to allow others to use your version 
022    of this file under the MPL, indicate your decision by deleting  the provisions above 
023    and replace  them with the notice and other provisions required by the GPL License.  
024    If you do not delete the provisions above, a recipient may use your version of 
025    this file under either the MPL or the GPL. 
026     */
027    
028    package ca.uhn.hl7v2.app;
029    
030    import java.io.IOException;
031    import java.util.Date;
032    import java.util.GregorianCalendar;
033    
034    import ca.uhn.hl7v2.HL7Exception;
035    import ca.uhn.hl7v2.model.DataTypeException;
036    import ca.uhn.hl7v2.model.Message;
037    import ca.uhn.hl7v2.model.Segment;
038    import ca.uhn.hl7v2.model.Structure;
039    import ca.uhn.hl7v2.model.primitive.CommonTS;
040    import ca.uhn.hl7v2.parser.DefaultModelClassFactory;
041    import ca.uhn.hl7v2.parser.ModelClassFactory;
042    import ca.uhn.hl7v2.parser.Parser;
043    import ca.uhn.hl7v2.util.MessageIDGenerator;
044    import 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     */
053    public 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    }