1
2
3
4 package ca.uhn.hl7v2.protocol.impl;
5
6 import java.io.IOException;
7 import java.util.ArrayList;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.regex.Pattern;
11
12 import ca.uhn.hl7v2.AcknowledgmentCode;
13 import ca.uhn.hl7v2.HL7Exception;
14 import ca.uhn.hl7v2.HapiContext;
15 import ca.uhn.hl7v2.Version;
16 import ca.uhn.hl7v2.app.DefaultApplication;
17 import ca.uhn.hl7v2.model.GenericMessage;
18 import ca.uhn.hl7v2.model.Message;
19 import ca.uhn.hl7v2.model.Segment;
20 import ca.uhn.hl7v2.parser.GenericParser;
21 import ca.uhn.hl7v2.parser.Parser;
22 import ca.uhn.hl7v2.protocol.*;
23 import ca.uhn.hl7v2.util.DeepCopy;
24 import ca.uhn.hl7v2.util.Terser;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28
29
30
31
32
33
34 public class ApplicationRouterImpl implements ApplicationRouter {
35
36
37
38
39
40 public static final AcknowledgmentCode DEFAULT_EXCEPTION_ACKNOWLEDGEMENT_CODE = AcknowledgmentCode.AE;
41
42 private static final Logger log = LoggerFactory.getLogger(ApplicationRouterImpl.class);
43
44
45
46
47
48 public static final String RAW_MESSAGE_KEY = MetadataKeys.IN_RAW_MESSAGE;
49
50 private List<Binding> myBindings;
51 private Parser myParser;
52 private ReceivingApplicationExceptionHandler myExceptionHandler;
53 private final HapiContext myContext;
54 private AcknowledgmentCode defaultAcknowledgementMode = DEFAULT_EXCEPTION_ACKNOWLEDGEMENT_CODE;
55
56
57
58
59
60 @Deprecated
61 public ApplicationRouterImpl() {
62 this(new GenericParser());
63 }
64
65
66
67
68
69
70
71 public ApplicationRouterImpl(Parser theParser) {
72 this(theParser.getHapiContext(), theParser);
73 }
74
75 public ApplicationRouterImpl(HapiContext theContext) {
76 this(theContext, theContext.getGenericParser());
77 }
78
79
80
81
82
83
84
85
86
87 public ApplicationRouterImpl(HapiContext theContext, Parser theParser) {
88 init(theParser);
89 myContext = theContext;
90 }
91
92 private void init(Parser theParser) {
93 myBindings = new ArrayList<>(20);
94 myParser = theParser;
95 }
96
97 public void setDefaultAcknowledgementMode(AcknowledgmentCode defaultAcknowledgementMode) {
98 this.defaultAcknowledgementMode = defaultAcknowledgementMode;
99 }
100
101
102
103
104 public Transportable../ca/uhn/hl7v2/protocol/Transportable.html#Transportable">Transportable processMessage(Transportable theMessage) throws HL7Exception {
105 String[] result = processMessage(theMessage.getMessage(), theMessage.getMetadata());
106 Transportable response = new TransportableImpl(result[0]);
107
108 if (result[1] != null) {
109 response.getMetadata().put(METADATA_KEY_MESSAGE_CHARSET, result[1]);
110 }
111
112 return response;
113 }
114
115
116
117
118
119
120
121
122
123
124 private String[] processMessage(String incomingMessageString, Map<String, Object> theMetadata) throws HL7Exception {
125 Logger rawOutbound = LoggerFactory.getLogger("ca.uhn.hl7v2.raw.outbound");
126 Logger rawInbound = LoggerFactory.getLogger("ca.uhn.hl7v2.raw.inbound");
127
128
129
130
131 log.debug("ApplicationRouterImpl got message: {}", incomingMessageString);
132 rawInbound.debug(incomingMessageString);
133
134 Message incomingMessageObject = null;
135 String outgoingMessageString = null;
136 String outgoingMessageCharset = null;
137 try {
138 incomingMessageObject = myParser.parse(incomingMessageString);
139
140 Terserml#Terser">Terser inTerser = new Terser(incomingMessageObject);
141 theMetadata.put(MetadataKeys.IN_MESSAGE_CONTROL_ID, inTerser.get("/.MSH-10"));
142
143 } catch (HL7Exception e) {
144 log.debug("Exception parsing incoming message", e);
145 try {
146 outgoingMessageString = logAndMakeErrorMessage(e, myParser.getCriticalResponseData(incomingMessageString), myParser, myParser.getEncoding(incomingMessageString));
147 } catch (HL7Exception e2) {
148 log.error("Exception occurred while logging parse failure", e2);
149 outgoingMessageString = null;
150 }
151 if (myExceptionHandler != null) {
152 outgoingMessageString = myExceptionHandler.processException(incomingMessageString, theMetadata, outgoingMessageString, e);
153 if (outgoingMessageString == null) {
154 throw new HL7Exception("Application exception handler may not return null");
155 }
156 }
157 }
158
159
160 if (outgoingMessageString == null) {
161 try {
162
163 String check = System.getProperty("ca.uhn.hl7v2.protocol.impl.check_parse");
164 if (check != null && check.equals("TRUE")) {
165 ParseChecker.checkParse(incomingMessageString, incomingMessageObject, myParser);
166 }
167
168
169
170 ReceivingApplication<Message> app = findApplication(incomingMessageObject);
171 theMetadata.put(RAW_MESSAGE_KEY, incomingMessageString);
172
173 log.debug("Sending message to application: {}", app.toString());
174 Message response = app.processMessage(incomingMessageObject, theMetadata);
175
176
177 outgoingMessageString = myParser.encode(response, myParser.getEncoding(incomingMessageString));
178
179 Terserrser.html#Terser">Terser t = new Terser(response);
180 outgoingMessageCharset = t.get(METADATA_KEY_MESSAGE_CHARSET);
181 } catch (Exception e) {
182 outgoingMessageString = handleProcessMessageException(incomingMessageString, theMetadata, incomingMessageObject, e);
183 } catch (Error e) {
184 log.debug("Caught runtime exception of type {}, going to wrap it as HL7Exception and handle it", e.getClass());
185 HL7ExceptionL7Exception">HL7Exception wrapped = new HL7Exception(e);
186 outgoingMessageString = handleProcessMessageException(incomingMessageString, theMetadata, incomingMessageObject, wrapped);
187 }
188 }
189
190 log.debug("ApplicationRouterImpl sending message: {}", outgoingMessageString);
191 rawOutbound.debug(outgoingMessageString);
192
193 return new String[]{outgoingMessageString, outgoingMessageCharset};
194 }
195
196 private String handleProcessMessageException(String incomingMessageString, Map<String, Object> theMetadata, Message incomingMessageObject, Exception e) throws HL7Exception {
197 String outgoingMessageString;
198 SegmentSegmentader = incomingMessageObject != null ? (Segment) incomingMessageObject.get("MSH") : null;
199 outgoingMessageString = logAndMakeErrorMessage(e, inHeader, myParser, myParser.getEncoding(incomingMessageString));
200 if (outgoingMessageString != null && myExceptionHandler != null) {
201 outgoingMessageString = myExceptionHandler.processException(incomingMessageString, theMetadata, outgoingMessageString, e);
202 }
203 return outgoingMessageString;
204 }
205
206
207
208
209
210 public boolean hasActiveBinding(AppRoutingData theRoutingData) {
211 boolean result = false;
212 ReceivingApplication<? extends Message> app = findDestination(null, theRoutingData);
213 if (app != null) {
214 result = true;
215 }
216 return result;
217 }
218
219
220
221
222
223
224 private <T extends Message> ReceivingApplication<T> findDestination(T theMessage, AppRoutingData theRoutingData) {
225 ReceivingApplication<? extends Message> result = null;
226 for (int i = 0; i < myBindings.size() && result == null; i++) {
227 Binding binding = myBindings.get(i);
228 if (matches(theRoutingData, binding.routingData) && binding.active) {
229 if (theMessage == null || ((ReceivingApplication<T>)binding.application).canProcess(theMessage)) {
230 result = binding.application;
231 }
232 }
233 }
234 return (ReceivingApplication<T>)result;
235 }
236
237
238
239
240
241 private Binding findBinding(ReceivingApplication<? extends Message> application) {
242 Binding result = null;
243 for (int i = 0; i < myBindings.size() && result == null; i++) {
244 Binding binding = myBindings.get(i);
245 if (application == binding.application) {
246 result = binding;
247 }
248 }
249 return result;
250 }
251
252
253
254
255
256 private Binding findBinding(AppRoutingData theRoutingData) {
257 Binding result = null;
258 for (int i = 0; i < myBindings.size() && result == null; i++) {
259 Binding binding = myBindings.get(i);
260 if (theRoutingData.equals(binding.routingData)) {
261 result = binding;
262 }
263 }
264 return result;
265 }
266
267
268
269
270
271
272 public void bindApplication(AppRoutingData theRoutingData, ReceivingApplication<? extends Message> theApplication) {
273 Binding binding = new Binding(theRoutingData, true, theApplication);
274 myBindings.add(binding);
275 }
276
277
278
279
280
281 public boolean unbindApplication(AppRoutingData theRoutingData) {
282 Binding b = findBinding(theRoutingData);
283 return b != null && myBindings.remove(b);
284 }
285
286
287
288
289 public boolean unbindApplication(ReceivingApplication<? extends Message> theApplication) {
290 Binding b = findBinding(theApplication);
291 return b != null && myBindings.remove(b);
292 }
293
294
295
296
297 public void disableBinding(AppRoutingData theRoutingData) {
298 Binding b = findBinding(theRoutingData);
299 if (b != null) {
300 b.active = false;
301 }
302 }
303
304
305
306
307 public void enableBinding(AppRoutingData theRoutingData) {
308 Binding b = findBinding(theRoutingData);
309 if (b != null) {
310 b.active = true;
311 }
312 }
313
314
315
316
317 public Parser getParser() {
318 return myParser;
319 }
320
321
322
323
324 public void setExceptionHandler(ReceivingApplicationExceptionHandler theExceptionHandler) {
325 this.myExceptionHandler = theExceptionHandler;
326 }
327
328
329
330
331
332
333
334
335 public static boolean matches(AppRoutingData theMessageData,
336 AppRoutingData theReferenceData) {
337
338 boolean result = false;
339
340 if (matches(theMessageData.getMessageType(), theReferenceData.getMessageType())
341 && matches(theMessageData.getTriggerEvent(), theReferenceData.getTriggerEvent())
342 && matches(theMessageData.getProcessingId(), theReferenceData.getProcessingId())
343 && matches(theMessageData.getVersion(), theReferenceData.getVersion())) {
344
345 result = true;
346 }
347
348 return result;
349 }
350
351
352 private static boolean matches(String theMessageData, String theReferenceData) {
353 boolean result = false;
354
355 String messageData = theMessageData;
356 if (messageData == null) {
357 messageData = "";
358 }
359
360 if (messageData.equals(theReferenceData) ||
361 theReferenceData.equals("*") ||
362 Pattern.matches(theReferenceData, messageData)) {
363 result = true;
364 }
365 return result;
366 }
367
368
369
370
371 private <T extends Message> ReceivingApplication<T> findApplication(T theMessage) throws HL7Exception {
372 Terserrser.html#Terser">Terser t = new Terser(theMessage);
373 AppRoutingData msgData =
374 new AppRoutingDataImpl(t.get("/MSH-9-1"), t.get("/MSH-9-2"), t.get("/MSH-11-1"), t.get("/MSH-12"));
375
376 ReceivingApplication<T> app = findDestination(theMessage, msgData);
377
378
379 if (app == null) {
380 app = (ReceivingApplication<T>)new DefaultApplication();
381 }
382
383 return app;
384 }
385
386
387
388
389 private static class Binding {
390 public final AppRoutingData routingData;
391 public boolean active;
392 public final ReceivingApplication<? extends Message> application;
393
394 public Binding(AppRoutingData theRoutingData, boolean isActive, ReceivingApplication<? extends Message> theApplication) {
395 routingData = theRoutingData;
396 active = isActive;
397 application = theApplication;
398 }
399 }
400
401
402
403
404
405
406
407
408
409
410
411
412
413 public String logAndMakeErrorMessage(Exception e, Segment inHeader,
414 Parser p, String encoding) throws HL7Exception {
415
416 switch (myContext.getServerConfiguration().getApplicationExceptionPolicy()) {
417 case DO_NOT_RESPOND:
418 log.error("Application exception detected, not going to send a response back to the client", e);
419 return null;
420 case DEFAULT:
421 default:
422 log.error("Attempting to send error message to remote system.", e);
423 break;
424 }
425
426 HL7Exceptiona/uhn/hl7v2/HL7Exception.html#HL7Exception">HL7Exception hl7e = e instanceof HL7Exception ?
427 (HL7Exception) e :
428 new HL7Exception(e.getMessage(), e);
429
430 try {
431 Message out = hl7e.getResponseMessage();
432 if (out == null) {
433 Message in = getInMessage(inHeader);
434 out = in.generateACK(defaultAcknowledgementMode, hl7e);
435 }
436 return encoding != null ? p.encode(out, encoding) : p.encode(out);
437
438 } catch (IOException ioe) {
439 throw new HL7Exception(
440 "IOException creating error response message: "
441 + ioe.getMessage());
442 }
443
444 }
445
446 private Message getInMessage(Segment inHeader) throws HL7Exception, IOException {
447 Message in;
448 if (inHeader != null) {
449 in = inHeader.getMessage();
450
451 DeepCopy.copy(inHeader, (Segment) in.get("MSH"));
452 } else {
453 in = Version.highestAvailableVersionOrDefault().newGenericMessage(myParser.getFactory());
454 ((GenericMessage) in).initQuickstart("ACK", "", "");
455 }
456 return in;
457 }
458
459
460 }