1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  package uk.nhs.interoperability.consumer;
15  
16  import java.io.IOException;
17  
18  import javax.servlet.ServletException;
19  import javax.servlet.http.HttpServletRequest;
20  import javax.servlet.http.HttpServletResponse;
21  import javax.xml.parsers.ParserConfigurationException;
22  import javax.xml.xpath.XPathConstants;
23  import javax.xml.xpath.XPathExpressionException;
24  
25  import org.w3c.dom.Document;
26  import org.w3c.dom.Node;
27  import org.xml.sax.SAXException;
28  
29  import uk.nhs.interoperability.infrastructure.ITKCommsException;
30  import uk.nhs.interoperability.infrastructure.ITKIdentityImpl;
31  import uk.nhs.interoperability.infrastructure.ITKMessageProperties;
32  import uk.nhs.interoperability.infrastructure.ITKMessagePropertiesImpl;
33  import uk.nhs.interoperability.infrastructure.ITKMessagingException;
34  import uk.nhs.interoperability.payload.ITKMessage;
35  import uk.nhs.interoperability.payload.SimpleMessage;
36  import uk.nhs.interoperability.source.ITKCallbackHandler;
37  import uk.nhs.interoperability.transport.WS.ITKSOAPException;
38  import uk.nhs.interoperability.util.Logger;
39  import uk.nhs.interoperability.util.ServletUtils;
40  import uk.nhs.interoperability.util.xml.DomUtils;
41  import uk.nhs.interoperability.util.xml.XPaths;
42  
43  import com.xmlsolutions.annotation.Requirement;
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  @SuppressWarnings("serial")
56  public abstract class AbstractCallbackListenerServlet extends ITKServlet {
57  
58  	
59  	
60  	private ITKCallbackHandler callbackHandler;
61  	
62  	
63  
64  
65  	@Override
66  	public void init() throws ServletException {
67  		this.callbackHandler = getCallbackHandler();
68  		super.init();
69  	}
70  	
71  	
72  
73  
74  
75  
76  	public abstract ITKCallbackHandler getCallbackHandler();
77  	
78  	
79  
80  
81  	@Override
82  	@Requirement(traceTo={"WS-PAT-02"})
83  	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
84  		Logger.debug("Received a message"); 
85  		String requestString = new String(ServletUtils.readRequestContent(req));
86  		try {
87  			this.processSOAPMessage(requestString);
88  			resp.setStatus(HttpServletResponse.SC_ACCEPTED);
89  			Logger.debug("Completed processing");
90  			return;
91  		} catch (ITKMessagingException e) {
92  			Logger.error("Could not process Callback message", e);
93  			resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
94  			resp.getWriter().write(new ITKSOAPException(e).serialiseXML());
95  		} catch (Throwable t) {
96  			Logger.error("Could not process Callback message - general error", t);
97  			resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
98  			resp.getWriter().write(t.getLocalizedMessage());
99  		}
100 	}
101 	
102 	
103 	
104 	
105 
106 
107 
108 
109 
110 	@Requirement(traceTo={"WS-PAT-02"})
111 	private void processSOAPMessage(String requestString) throws ITKMessagingException {	
112 
113 		ITKMessageProperties itkMessageProperties = null;
114 		ITKMessage requestMsg = null;
115 
116 		try {
117 			
118 			Document requestDocument = DomUtils.parse(requestString);
119 
120 			if (requestDocument == null){
121 				
122 				
123 				ITKMessagingException unknownRequestException = new ITKMessagingException(
124 						ITKMessagingException.PROCESSING_ERROR_NOT_RETRYABLE_CODE, "No Content Received.");
125 				Logger.error("Unknown request type",unknownRequestException);
126 				throw unknownRequestException;
127 				
128 			} else {
129 				
130 				
131 				Logger.trace(DomUtils.serialiseToXML(requestDocument, DomUtils.PRETTY_PRINT));
132 
133 				
134 				Document businessPayloadDocument = DomUtils.createDocumentFromNode((Node)XPaths.SOAP_BODY_CONTENT_XPATH.evaluate(requestDocument, XPathConstants.NODE));
135 
136 				if (businessPayloadDocument == null){
137 					
138 					ITKMessagingException unknownRequestException = new ITKMessagingException(
139 							ITKMessagingException.PROCESSING_ERROR_NOT_RETRYABLE_CODE, "No payload in SOAP Body");
140 					Logger.error("Unknown request type",unknownRequestException);
141 					throw unknownRequestException;
142 				}
143 				
144 				String requestPayloadString = DomUtils.serialiseToXML(businessPayloadDocument);
145 				
146 				
147 				requestMsg = new SimpleMessage();
148 				requestMsg.setBusinessPayload(requestPayloadString);
149 				
150 				processITKMessage(requestMsg);
151 
152 			}
153 	
154 		} catch (XPathExpressionException e) {
155 			throw new ITKMessagingException(itkMessageProperties, ITKMessagingException.PROCESSING_ERROR_NOT_RETRYABLE_CODE, "Could not extract values from request", e);
156 		} catch (IOException e) {
157 			throw new ITKMessagingException(itkMessageProperties, ITKMessagingException.PROCESSING_ERROR_NOT_RETRYABLE_CODE, "Could not parse request", e);
158 		} catch (ParserConfigurationException e) {
159 			throw new ITKMessagingException(itkMessageProperties, ITKMessagingException.PROCESSING_ERROR_NOT_RETRYABLE_CODE, "XML parser configuration error", e);
160 		} catch (SAXException e) {
161 			throw new ITKMessagingException(itkMessageProperties, ITKMessagingException.PROCESSING_ERROR_NOT_RETRYABLE_CODE, "Error parsing request XML", e);
162 		}
163 	}
164 	
165 	
166 	
167 	
168 
169 
170 
171 
172 
173 	private void processITKMessage(ITKMessage requestMessage) throws ITKMessagingException {	
174 
175 		try {
176 			
177 			Document requestDocument = DomUtils.parse(requestMessage.getBusinessPayload());
178 			String requestPayloadName = requestDocument.getDocumentElement().getLocalName();
179 			Logger.trace("Received:"+requestPayloadName);
180 			
181 			if (requestPayloadName.equalsIgnoreCase("DistributionEnvelope")){
182 				
183 				Logger.trace("Processing DistributionEnvelope");
184 				processDistributionEnvelope(requestDocument);
185 				
186 			} else if (requestPayloadName.equalsIgnoreCase("SimpleMessageResponse")){
187 				
188 				Logger.trace("Processing SimpleMessageResponse");
189 				
190 				ITKMessage applicationMessage = new SimpleMessage();
191 				String payload = (String)XPaths.ITK_SIMPLE_MESSAGE_RESPONSE_CONTENT_XPATH.evaluate(requestDocument);	
192 				applicationMessage.setBusinessPayload(payload);
193 
194 				
195 				ITKMessageProperties itkMessageProperties = new ITKMessagePropertiesImpl();
196 				itkMessageProperties.setAuditIdentity(new ITKIdentityImpl("NOT SPECIFIED"));
197 				applicationMessage.setMessageProperties(itkMessageProperties);
198 				
199 				this.callbackHandler.onMessage(applicationMessage);
200 				
201 			} else {
202 				
203 				Logger.trace("Processing UNKNOWN");
204 				ITKMessagingException unknownResponseException = new ITKMessagingException(
205 						ITKMessagingException.PROCESSING_ERROR_NOT_RETRYABLE_CODE, "Neither DistributionEnvelope nor SimpleMessageResponse Found");
206 				Logger.error("Unknown response type",unknownResponseException);
207 				throw unknownResponseException;
208 				
209 			}
210 			
211 			
212 			
213 		} catch (SAXException se) {
214 			Logger.error("SAXException processing response from Transport", se);
215 			throw new ITKCommsException("XML Error Processing ITK Response");
216 		} catch (IOException ioe) {
217 			Logger.error("IOException on Transport", ioe);
218 			throw new ITKCommsException("Transport error sending ITK Message");
219 		} catch (ParserConfigurationException pce) {
220 			Logger.error("ParseConfigurationException on Transport", pce);
221 			throw new ITKCommsException("XML Configuration Error Processing ITK Response");
222 		} catch (XPathExpressionException xpe) {
223 			Logger.error("XPathExpressionException reading payload on Transport Response", xpe);
224 			throw new ITKCommsException("No Payload found in ITK Response");
225 		}
226 		
227 	}
228 
229 	
230 
231 
232 
233 
234 
235 	private void processDistributionEnvelope(Document distributionEnvelope) throws ITKMessagingException {	
236 
237 		ITKMessage applicationMessage = new SimpleMessage();
238 		
239 		try {
240 			
241 			
242 			ITKMessageProperties itkMessageProperties = ITKMessagePropertiesImpl.build(distributionEnvelope);
243 			applicationMessage.setMessageProperties(itkMessageProperties);
244 	
245 			
246 			String mimetype = (String)XPaths.ITK_FIRST_MIMETYPE_XPATH.evaluate(distributionEnvelope);
247 			Logger.debug("MimeType: " + mimetype);
248 			
249 			if (mimetype.equalsIgnoreCase("text/plain")){
250 				
251 				
252 				String payload = (String)XPaths.ITK_FIRST_PAYLOAD_TEXT_XPATH.evaluate(distributionEnvelope);
253 				
254 				
255 				String base64 = (String)XPaths.ITK_FIRST_BASE64_XPATH.evaluate(distributionEnvelope);
256 				Logger.debug("Base64: " + base64);
257 				if (base64.equalsIgnoreCase("true")){
258 					byte[] payloadBytes = javax.xml.bind.DatatypeConverter.parseBase64Binary(payload);
259 					applicationMessage.setBusinessPayload(new String(payloadBytes));
260 				} else {
261 					applicationMessage.setBusinessPayload(payload);
262 				}
263 				this.callbackHandler.onMessage(applicationMessage);
264 				
265 			} else {
266 				
267 				
268 				Document businessPayloadDocument = DomUtils.createDocumentFromNode((Node)XPaths.ITK_FIRST_PAYLOAD_XPATH.evaluate(distributionEnvelope, XPathConstants.NODE));
269 				
270 				if (businessPayloadDocument == null){
271 					
272 					ITKMessagingException unknownResponseException = new ITKMessagingException(
273 							ITKMessagingException.PROCESSING_ERROR_NOT_RETRYABLE_CODE, "No payload in Distribution Envelope");
274 					Logger.error("Unknown response type",unknownResponseException);
275 					throw unknownResponseException;
276 				}
277 	
278 				String businessPayloadName = distributionEnvelope.getDocumentElement().getLocalName();
279 				Logger.trace("ITK Payload 1:"+businessPayloadName);
280 				applicationMessage.setBusinessPayload(DomUtils.serialiseToXML(businessPayloadDocument));
281 				
282 				if (businessPayloadName.equalsIgnoreCase("InfrastructureResponse")){
283 					Logger.trace("Processing InfrastructureResponse");
284 					
285 					
286 					
287 	
288 					String result = distributionEnvelope.getDocumentElement().getAttribute("result");
289 					if (result.equals("OK")){
290 						
291 						
292 						this.callbackHandler.onAck(null);
293 					} else {
294 						
295 						this.callbackHandler.onNack(null);
296 					}
297 					
298 				} else if (businessPayloadName.equalsIgnoreCase("BusinessResponseMessage")){
299 					
300 					Logger.trace("Processing BusinessResponseMessage (BusinessACK)");
301 					
302 					
303 			
304 					
305 					
306 					this.callbackHandler.onMessage(applicationMessage);    
307 				
308 					
309 					this.sendInfrastructureAck(applicationMessage.getMessageProperties());
310 					
311 				}	else {
312 					
313 					
314 					this.callbackHandler.onMessage(applicationMessage);
315 	
316 				}
317 			}
318 		} catch (ITKMessagingException e){
319 			
320 		} catch (XPathExpressionException xpe) {
321 			Logger.error("XPathExpressionException reading DE on Transport Response", xpe);
322 			throw new ITKCommsException("Error processing Distribution Envelope");
323 		} catch (ParserConfigurationException pce) {
324 			Logger.error("ParseConfigurationException on DE", pce);
325 			throw new ITKCommsException("XML Configuration Error Processing Distribution Envelope");
326 		}
327 		
328 	}
329 	
330 }