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 }