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 }