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