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}