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.util.MessageIDGenerator;
042    import ca.uhn.hl7v2.util.Terser;
043    import ca.uhn.log.HapiLog;
044    import ca.uhn.log.HapiLogFactory;
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     * @author  Bryan Tripp
051     */
052    public class DefaultApplication implements Application {
053    
054        private static final HapiLog log = HapiLogFactory.getHapiLog(DefaultApplication.class);
055    
056        /** Creates a new instance of DefaultApplication */
057        public DefaultApplication() {
058        }
059    
060        /**
061         * Returns true.  
062         */
063        public boolean canProcess(Message in) {
064            return true;
065        }
066    
067        /**
068         * Creates and returns an acknowledgement -- the details are determined by fillDetails().
069         */
070        public Message processMessage(Message in) throws ApplicationException {
071            Message out = null;
072            try {
073                //get default ACK
074                out = makeACK((Segment) in.get("MSH"));
075                fillDetails(out);
076            } catch (Exception e) {
077                throw new ApplicationException("Couldn't create response message: " + e.getMessage());
078            }        
079            return out;
080        } 
081        
082        /**
083         * Fills in the details of an Application Reject message, including response and 
084         * error codes, and a text error message.  This is the method to override if you want
085         * to respond differently.  
086         */
087        public void fillDetails(Message ack) throws ApplicationException {
088            try {
089                //populate MSA and ERR with generic error ... 
090                Segment msa = (Segment) ack.get("MSA");
091                Terser.set(msa, 1, 0, 1, 1, "AR");
092                Terser.set(
093                    msa,
094                    3,
095                    0,
096                    1,
097                    1,
098                    "No appropriate destination could be found to which this message could be routed.");
099                //this is max length
100    
101                //populate ERR segment if it exists (may not depending on version)
102                Structure s = ack.get("ERR");
103                if (s != null) {
104                    Segment err = (Segment) s;
105                    Terser.set(err, 1, 0, 4, 1, "207");
106                    Terser.set(err, 1, 0, 4, 2, "Application Internal Error");
107                    Terser.set(err, 1, 0, 4, 3, "HL70357");
108                }
109    
110            }
111            catch (Exception e) {
112                throw new ApplicationException("Error trying to create Application Reject message: " + e.getMessage());
113            }
114        }
115    
116        /**
117         * Creates an ACK message with the minimum required information from an inbound message.  
118         * Optional fields can be filled in afterwards, before the message is returned.  Pleaase   
119         * note that MSH-10, the outbound message control ID, is also set using the class 
120         * <code>ca.uhn.hl7v2.util.MessageIDGenerator</code>.  Also note that the ACK messages returned
121         * is the same version as the version stated in the inbound MSH if there is a generic ACK for that
122         * version, otherwise a version 2.4 ACK is returned. MSA-1 is set to AA by default.  
123         *
124         * @param inboundHeader the MSH segment if the inbound message
125         * @throws IOException if there is a problem reading or writing the message ID file
126         * @throws DataTypeException if there is a problem setting ACK values
127         */
128        public static Message makeACK(Segment inboundHeader) throws HL7Exception, IOException {
129            if (!inboundHeader.getName().equals("MSH"))
130                throw new HL7Exception(
131                    "Need an MSH segment to create a response ACK (got " + inboundHeader.getName() + ")");
132    
133            //make ACK of correct version
134            String version = null;
135            try {
136                version = Terser.get(inboundHeader, 12, 0, 1, 1);
137            }
138            catch (HL7Exception e) { /* proceed with null */
139            }
140            if (version == null) version = "2.4";
141    
142            String ackClassName = DefaultModelClassFactory.getVersionPackageName(version) + "message.ACK";
143    
144            Message out = null;
145            try {
146                Class ackClass = Class.forName(ackClassName);
147                out = (Message) ackClass.newInstance();
148            }
149            catch (Exception e) {
150                throw new HL7Exception("Can't instantiate ACK of class " + ackClassName + ": " + e.getClass().getName());
151            }
152            Terser terser = new Terser(out);
153    
154            //populate outbound MSH using data from inbound message ...             
155            Segment outHeader = (Segment) out.get("MSH");
156            fillResponseHeader(inboundHeader, outHeader);
157    
158            terser.set("/MSH-9", "ACK");
159            terser.set("/MSH-12", version);
160            terser.set("/MSA-1", "AA");
161            terser.set("/MSA-2", terser.get(inboundHeader, 10, 0, 1, 1));
162    
163            return out;
164        }
165    
166        /** 
167         * Populates certain required fields in a response message header, using 
168         * information from the corresponding inbound message.  The current time is 
169         * used for the message time field, and <code>MessageIDGenerator</code> is 
170         * used to create a unique message ID.  Version and message type fields are 
171         * not populated.  
172         */
173        public static void fillResponseHeader(Segment inbound, Segment outbound) throws HL7Exception, IOException {
174            if (!inbound.getName().equals("MSH") || !outbound.getName().equals("MSH"))
175                throw new HL7Exception("Need MSH segments.  Got " + inbound.getName() + " and " + outbound.getName());
176    
177            //get MSH data from incoming message ...        
178            String encChars = Terser.get(inbound, 2, 0, 1, 1);
179            String fieldSep = Terser.get(inbound, 1, 0, 1, 1);
180            String procID = Terser.get(inbound, 11, 0, 1, 1);
181    
182            //populate outbound MSH using data from inbound message ...                     
183            Terser.set(outbound, 2, 0, 1, 1, encChars);
184            Terser.set(outbound, 1, 0, 1, 1, fieldSep);
185            GregorianCalendar now = new GregorianCalendar();
186            now.setTime(new Date());
187            Terser.set(outbound, 7, 0, 1, 1, CommonTS.toHl7TSFormat(now));
188            Terser.set(outbound, 10, 0, 1, 1, MessageIDGenerator.getInstance().getNewID());
189            Terser.set(outbound, 11, 0, 1, 1, procID);
190        }
191    
192    }