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 }