1
2
3
4
5
6
7
8
9
10
11
12
13
14 package uk.nhs.interoperability.consumer.appemulator;
15
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.util.HashMap;
19 import java.util.Map;
20 import java.util.Properties;
21 import java.util.Queue;
22 import java.util.Scanner;
23 import java.util.UUID;
24 import java.util.concurrent.ExecutorService;
25 import java.util.concurrent.Executors;
26 import java.util.concurrent.LinkedBlockingQueue;
27
28 import uk.nhs.interoperability.consumer.ITKMessageConsumer;
29 import uk.nhs.interoperability.infrastructure.ITKAddressImpl;
30 import uk.nhs.interoperability.infrastructure.ITKMessageProperties;
31 import uk.nhs.interoperability.infrastructure.ITKMessagingException;
32 import uk.nhs.interoperability.payload.ITKMessage;
33 import uk.nhs.interoperability.payload.ITKSimpleMessageResponse;
34 import uk.nhs.interoperability.payload.SimpleMessage;
35 import uk.nhs.interoperability.source.ITKMessageSender;
36 import uk.nhs.interoperability.source.ITKMessageSenderImpl;
37 import uk.nhs.interoperability.transform.TransformManager;
38 import uk.nhs.interoperability.util.HL7Utils;
39 import uk.nhs.interoperability.util.Logger;
40
41
42
43
44
45
46
47
48 public class SimpleApplicationEmulator implements ITKMessageConsumer, Runnable {
49
50
51 private static final String FROMADDRESS = "urn:nhs-uk:addressing:ods:TESTORGS:ORGA";
52
53
54 private ExecutorService executorService;
55
56
57 private boolean isRunning = true;
58
59
60 private ITKMessageSender itkMessageSender;
61
62
63 private Properties props = new Properties();
64
65
66 private String auditIdentity;
67
68
69
70
71
72
73
74
75 private Queue<ITKMessage> asyncProcessingQueue = new LinkedBlockingQueue<ITKMessage>();
76
77
78
79
80 public SimpleApplicationEmulator() {
81 this.executorService = Executors.newFixedThreadPool(1);
82 this.executorService.execute(this);
83 this.itkMessageSender = new ITKMessageSenderImpl();
84 this.props = new Properties();
85 try {
86 props.load(this.getClass().getResourceAsStream("/consumeremulator.properties"));
87 } catch (IOException e) {
88 Logger.error("Could not load consumer emulator properties - emulator not likely to behave correctly", e);
89 }
90 this.auditIdentity = this.props.getProperty("audit.identity");
91 }
92
93
94
95
96 @Override
97 public ITKMessage onSyncMessage(ITKMessage request) throws ITKMessagingException {
98 Logger.debug("Application invoked");
99 ITKMessage response = this.processMessage(request);
100 response.getMessageProperties().setFromAddress(new ITKAddressImpl(FROMADDRESS));
101
102 return response;
103 }
104
105
106
107
108 @Override
109 public void onMessage(ITKMessage request) throws ITKMessagingException {
110
111 if (request == null) {
112 throw new ITKMessagingException("The request was null - could not process");
113 }
114 if (request.getBusinessPayload() == null && request.getMessageProperties() == null) {
115 throw new ITKMessagingException(request.getMessageProperties(), ITKMessagingException.INVALID_MESSAGE_CODE, "The request message properties or contents were null - message cannot be processed");
116 }
117
118 this.asyncProcessingQueue.add(request);
119 }
120
121
122
123
124
125
126 private void processAsyncMessage(ITKMessage request) {
127 Logger.debug("Processing queued itk request");
128 try {
129 ITKMessage response = this.processMessage(request);
130 if (response != null) {
131 response.getMessageProperties().setFromAddress(new ITKAddressImpl(FROMADDRESS));
132 this.itkMessageSender.send(response);
133 } else {
134 Logger.info("No response configured/created for " + request);
135 }
136 } catch (ITKMessagingException e) {
137
138
139
140
141
142
143 Logger.error("Could not send aysnchronous response", e);
144 }
145 }
146
147
148
149
150
151
152
153
154 private ITKMessage processMessage(ITKMessage request) throws ITKMessagingException {
155
156 String requestMsgService = request.getMessageProperties().getServiceId();
157
158
159
160
161
162 String responseType = this.props.getProperty(requestMsgService + ".response.type");
163
164
165 if (responseType == null) {
166
167 throw new ITKMessagingException("Incorrect emulator configuration - no response type configured for " + requestMsgService);
168
169 } else if (responseType.equalsIgnoreCase("simple")) {
170
171 Logger.trace("Creating a simple message response");
172 return new ITKSimpleMessageResponse(request.getMessageProperties(), true);
173
174 } else if (responseType.equalsIgnoreCase("fullResponse")) {
175
176 String responseProfileId = this.props.getProperty(request.getMessageProperties().getServiceId() + "Response.profileId");
177 Logger.trace("Creating a business response");
178 return this.turnaroundViaXSLT(responseProfileId, request);
179
180 } else if (responseType.equalsIgnoreCase("fixedResponse")) {
181
182 String responseProfileId = this.props.getProperty(request.getMessageProperties().getServiceId() + "Response.profileId");
183 Logger.trace("Creating a Fixed response");
184 return this.turnaroundViaFile(responseProfileId, request);
185
186 } else if (responseType.equalsIgnoreCase("businessAck")) {
187
188 return this.createBusinessAck(request);
189 } else if (responseType.equalsIgnoreCase("none")) {
190
191 return null;
192 } else {
193
194 throw new ITKMessagingException("Incorrect emulator configuration - unknown response type (" + responseType + ") configured for " + requestMsgService);
195 }
196 }
197
198
199
200
201
202
203
204
205 private ITKMessage createBusinessAck(ITKMessage request) throws ITKMessagingException {
206
207 String businessAckHandlingSpec = request.getMessageProperties().getHandlingSpecification(ITKMessageProperties.BUSINESS_ACK_HANDLING_SPECIFICATION_KEY);
208 if (businessAckHandlingSpec != null && businessAckHandlingSpec.equals("true")) {
209 String businessAckService = "urn:nhs-itk:services:201005:SendBusinessAck-v1-0";
210 String responseProfileId = this.props.getProperty(businessAckService + ".profileId");
211 Logger.trace("Creating a business response");
212 ITKMessage msg = this.turnaroundViaXSLT(responseProfileId, request);
213 msg.getMessageProperties().setServiceId(businessAckService);
214 return msg;
215 } else {
216 Logger.trace("No handling specification for business ack - not creating a business Ack");
217 }
218
219 return null;
220 }
221
222
223
224
225
226
227
228
229
230 private ITKMessage turnaroundViaXSLT(String responseProfileId, ITKMessage request) throws ITKMessagingException {
231
232 String xslt = this.props.getProperty(request.getMessageProperties().getServiceId() + ".turnaround.xslt");
233 if (xslt != null) {
234
235
236 SimpleMessage msg = new SimpleMessage(request.getMessageProperties(), this.auditIdentity, responseProfileId, true);
237 UUID messageId = UUID.randomUUID();
238 msg.getMessageProperties().setBusinessPayloadId(messageId.toString().toUpperCase());
239
240 Logger.trace("Using " + xslt + " to turnaround request");
241 String inputXML = request.getBusinessPayload();
242
243 String outputXML = TransformManager.doTransform(xslt, inputXML, this.getTransformParameters(msg));
244
245 msg.setBusinessPayload(outputXML);
246
247 return msg;
248 } else {
249 Logger.warn("Could not use XSLT to turnaround request");
250 }
251 return null;
252 }
253
254
255
256
257
258
259
260
261
262 private ITKMessage turnaroundViaFile(String responseProfileId, ITKMessage request) throws ITKMessagingException {
263
264
265 String fileName = this.props.getProperty(request.getMessageProperties().getServiceId() + ".turnaround.txt");
266 if (fileName != null) {
267
268
269
270 SimpleMessage msg = new SimpleMessage(request.getMessageProperties(), this.auditIdentity, responseProfileId, true);
271 UUID messageId = UUID.randomUUID();
272 msg.getMessageProperties().setBusinessPayloadId(messageId.toString().toUpperCase());
273
274 Logger.trace("Using " + fileName + " to turnaround request");
275 String responseString;
276 try {
277 responseString = readFile(fileName);
278 } catch (IOException e) {
279 e.printStackTrace();
280 throw new ITKMessagingException("Incorrect emulator configuration - error reading response file (" + fileName);
281 }
282 msg.setBusinessPayload(responseString);
283 return msg;
284
285 } else {
286 Logger.warn("Could not load turnaround request from file.");
287 }
288 return null;
289 }
290
291
292
293
294
295
296
297
298
299
300 private Map<String, String> getTransformParameters(ITKMessage itkMessage) {
301 if (itkMessage != null && itkMessage.getMessageProperties() != null) {
302 ITKMessageProperties msgProps = itkMessage.getMessageProperties();
303 Map<String, String> params = new HashMap<String, String>();
304 params.put("response-msg-id", msgProps.getBusinessPayloadId());
305 params.put("from-address", msgProps.getFromAddress().getURI());
306 params.put("to-address", msgProps.getToAddress().getURI());
307 params.put("creation-time", HL7Utils.getHL7DateTime());
308 return params;
309 }
310 return null;
311 }
312
313
314
315
316 @Override
317 public void run() {
318 while (this.isRunning) {
319 try {
320 if (this.asyncProcessingQueue.isEmpty()) {
321 Thread.sleep(5000);
322 } else {
323 this.processAsyncMessage(this.asyncProcessingQueue.poll());
324 }
325 } catch (InterruptedException e) {
326 this.isRunning = false;
327 }
328 }
329 }
330
331
332
333
334
335
336
337
338 private String readFile(String fname) throws IOException {
339
340 InputStream tis = this.getClass().getResourceAsStream("/messages/"+fname);
341 StringBuilder fileContents = new StringBuilder();
342 Scanner scanner = new Scanner(tis);
343 String lineSeparator = System.getProperty("line.separator");
344
345 try {
346 while(scanner.hasNextLine()) {
347 fileContents.append(scanner.nextLine() + lineSeparator);
348 }
349 return fileContents.toString();
350 } finally {
351 scanner.close();
352 }
353 }
354
355 }