1
2
3
4
5
6
7
8
9
10
11
12
13
14 package uk.nhs.interoperability.transport.WS;
15
16 import java.io.BufferedInputStream;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.io.PrintWriter;
20 import java.net.HttpURLConnection;
21 import java.net.MalformedURLException;
22 import java.net.SocketTimeoutException;
23 import java.net.URL;
24 import java.net.URLConnection;
25
26 import javax.servlet.http.HttpServletResponse;
27 import javax.xml.parsers.ParserConfigurationException;
28 import javax.xml.xpath.XPathConstants;
29 import javax.xml.xpath.XPathExpressionException;
30
31 import org.w3c.dom.Document;
32 import org.w3c.dom.Node;
33 import org.xml.sax.SAXException;
34
35 import uk.nhs.interoperability.infrastructure.ITKCommsException;
36 import uk.nhs.interoperability.infrastructure.ITKMessagingException;
37 import uk.nhs.interoperability.infrastructure.ITKTransportTimeoutException;
38 import uk.nhs.interoperability.payload.ITKMessage;
39 import uk.nhs.interoperability.payload.SimpleMessage;
40 import uk.nhs.interoperability.transport.ITKSender;
41 import uk.nhs.interoperability.transport.ITKTransportRoute;
42 import uk.nhs.interoperability.util.ITKApplicationProperties;
43 import uk.nhs.interoperability.util.Logger;
44 import uk.nhs.interoperability.util.xml.DomUtils;
45 import uk.nhs.interoperability.util.xml.XPaths;
46
47 import com.xmlsolutions.annotation.Requirement;
48
49
50
51
52
53
54
55
56 public class ITKSenderWSImpl implements ITKSender {
57
58
59 private static final String WSSOAP_FROM = "wssoap.from";
60
61
62 private static final String WSSECURITY_USERNAME = "wssecurity.username";
63
64
65
66
67 @Override
68 public void send(ITKTransportRoute destination, ITKMessage request)
69 throws ITKMessagingException {
70
71
72 WSSOAPMessageImpl message = null;
73
74
75
76 if (request.isResponse()) {
77 message = new WSSOAPMessageImpl(destination, request, WSSOAPMessageImpl.ASYNCRESP);
78 } else {
79 message = new WSSOAPMessageImpl(destination, request, WSSOAPMessageImpl.SYNCREQ);
80 }
81
82 message.setFrom(ITKApplicationProperties.getProperty(WSSOAP_FROM));
83 message.setTo(destination.getPhysicalAddress());
84 message.setUsername(ITKApplicationProperties.getProperty(WSSECURITY_USERNAME));
85 message.setTimeToLive(destination.getTimeToLive());
86
87 String SOAPPayload = message.getFullPayload();
88
89 Document responseDoc = transportSend(destination, SOAPPayload, message.getAction());
90
91 if (responseDoc==null){
92
93
94
95 } else {
96
97
98
99
100 ITKMessagingException unknownResponseException = new ITKMessagingException(
101 ITKMessagingException.PROCESSING_ERROR_NOT_RETRYABLE_CODE, "Unexpected Response");
102 Logger.error("Unexpected Response",unknownResponseException);
103 Logger.trace(DomUtils.serialiseToXML(responseDoc, DomUtils.PRETTY_PRINT));
104 throw unknownResponseException;
105
106 }
107
108
109 }
110
111
112
113
114 @Override
115 @Requirement(traceTo={"WS-PAT-01"})
116 public ITKMessage sendSync(ITKTransportRoute destination, ITKMessage request)
117 throws ITKMessagingException {
118
119
120 WSSOAPMessageImpl message = new WSSOAPMessageImpl(destination, request, WSSOAPMessageImpl.SYNCREQ);
121 message.setFrom(ITKApplicationProperties.getProperty(WSSOAP_FROM));
122 message.setTo(destination.getPhysicalAddress());
123 message.setUsername(ITKApplicationProperties.getProperty(WSSECURITY_USERNAME));
124 message.setTimeToLive(destination.getTimeToLive());
125
126 String SOAPPayload = message.getFullPayload();
127
128 try {
129
130 Document responseDoc = transportSend(destination, SOAPPayload, message.getAction());
131
132 if (responseDoc == null) {
133
134 ITKMessagingException unknownResponseException = new ITKMessagingException(ITKMessagingException.PROCESSING_ERROR_NOT_RETRYABLE_CODE, "HTTP Acknowledgement only received (202).");
135 Logger.error("Unknown response type",unknownResponseException);
136 throw unknownResponseException;
137
138 } else {
139
140
141 Logger.trace(DomUtils.serialiseToXML(responseDoc, DomUtils.PRETTY_PRINT));
142
143
144 Document businessPayloadDocument = DomUtils.createDocumentFromNode((Node)XPaths.SOAP_BODY_CONTENT_XPATH.evaluate(responseDoc, XPathConstants.NODE));
145
146 if (businessPayloadDocument == null){
147
148 ITKMessagingException unknownResponseException = new ITKMessagingException(
149 ITKMessagingException.PROCESSING_ERROR_NOT_RETRYABLE_CODE, "No payload in SOAP Body");
150 Logger.error("Unknown response type",unknownResponseException);
151 throw unknownResponseException;
152 }
153
154 String responsePayloadString = DomUtils.serialiseToXML(businessPayloadDocument);
155
156
157 return new SimpleMessage(responsePayloadString);
158
159 }
160 } catch (ParserConfigurationException pce) {
161 Logger.error("ParseConfigurationException on WS-CALL", pce);
162 throw new ITKCommsException("XML Configuration Error Processing ITK Response");
163 } catch (XPathExpressionException xpe) {
164 Logger.error("XPathExpressionException reading payload on WS Response", xpe);
165 throw new ITKCommsException("No Payload found in ITK Response");
166 }
167
168 }
169
170
171
172
173
174 @Override
175 @Requirement(traceTo={"WS-PAT-02"})
176 public void sendAysnc(ITKTransportRoute destination, ITKMessage request)
177 throws ITKMessagingException {
178
179
180 WSSOAPMessageImpl message = null;
181
182
183
184
185
186
187 message = new WSSOAPMessageImpl(destination, request, WSSOAPMessageImpl.ASYNCREQ);
188
189 message.setFrom(ITKApplicationProperties.getProperty(WSSOAP_FROM));
190 message.setTo(destination.getPhysicalAddress());
191 message.setUsername(ITKApplicationProperties.getProperty(WSSECURITY_USERNAME));
192 message.setTimeToLive(destination.getTimeToLive());
193
194 String SOAPPayload = message.getFullPayload();
195
196 try {
197
198 Document responseDoc = transportSend(destination, SOAPPayload, message.getAction());
199
200 if (responseDoc == null) {
201
202 } else {
203
204
205 Document businessPayload = DomUtils.createDocumentFromNode((Node)XPaths.SOAP_WRAPPED_ITK_SIMPLE_MESSAGE_RESPONSE_XPATH.evaluate(responseDoc, XPathConstants.NODE));
206
207
208 if (businessPayload==null){
209 ITKMessagingException unknownResponseException = new ITKMessagingException("Unknown Response Type");
210 Logger.error("Unknown response type",unknownResponseException);
211 Logger.trace(DomUtils.serialiseToXML(businessPayload, DomUtils.PRETTY_PRINT));
212 throw unknownResponseException;
213 }
214
215 }
216
217 } catch (ParserConfigurationException pce) {
218 Logger.error("ParseConfigurationException on WS-CALL", pce);
219 throw new ITKCommsException("XML Configuration Error Processing ITK Response");
220 } catch (XPathExpressionException xpe) {
221 Logger.error("XPathExpressionException reading payload on WS Response", xpe);
222 throw new ITKCommsException("No Payload found in ITK Response");
223 }
224
225 }
226
227
228
229
230
231
232
233
234
235 @Requirement(traceTo={"WS-PAT-01","WS-PAT-02"})
236 private Document transportSend(ITKTransportRoute destination, String SOAPPayload, String SOAPAction)
237 throws ITKMessagingException {
238
239 Document responseDoc = null;
240
241 String serviceEndpoint = destination.getPhysicalAddress();
242 try {
243 URLConnection urlConnection =
244 new URL(serviceEndpoint).openConnection();
245 HttpURLConnection conn = (HttpURLConnection) urlConnection;
246 conn.setUseCaches(false);
247 conn.setDoOutput(true);
248 conn.setDoInput(true);
249 conn.setRequestMethod("POST");
250 conn.setRequestProperty("Content-type","text/xml");
251 conn.setRequestProperty("accept-charset","UTF-8");
252 conn.setRequestProperty("SOAPAction",SOAPAction);
253
254 conn.setConnectTimeout(30000);
255 PrintWriter pw = new PrintWriter(conn.getOutputStream());
256 pw.write(SOAPPayload);
257 pw.close();
258 int responseCode = conn.getResponseCode();
259 Logger.trace("HTTP Response Code:"+responseCode);
260 if (responseCode == HttpServletResponse.SC_ACCEPTED){
261
262 Logger.trace("SIMPLE HTTP ACCEPT (202)");
263
264 } else if (responseCode == HttpServletResponse.SC_OK) {
265 Logger.trace("HTTP 200");
266 String responseString = readInput(conn.getInputStream());
267 responseDoc = DomUtils.parse(responseString);
268
269 } else if (responseCode == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) {
270 Logger.trace("HTTP 500");
271 String responseString = readInput(conn.getErrorStream());
272
273 if (responseString != null && responseString.contains("http://www.w3.org/2005/08/addressing/fault")) {
274
275 throw ITKSOAPException.parseSOAPFault(responseString);
276 } else {
277 throw new ITKCommsException("HTTP Internal server error");
278 }
279 } else {
280
281 Logger.trace("Unrecognized HTTP response code:"+responseCode);
282 throw new ITKCommsException("Unrecognized HTTP response code:"+responseCode);
283
284 }
285
286 } catch (MalformedURLException mue) {
287 Logger.error("MalformedURLException on WS-CALL", mue);
288 throw new ITKCommsException("Configuration error sending ITK Message");
289 } catch (SocketTimeoutException ste) {
290 Logger.error("Timeout on WS-CALL", ste);
291 throw new ITKTransportTimeoutException("Transport timeout sending ITK Message");
292 } catch (IOException ioe) {
293 Logger.error("IOException on WS-CALL", ioe);
294 throw new ITKCommsException("Transport error sending ITK Message");
295 } catch (SAXException se) {
296 Logger.error("SAXException processing response from WS-CALL", se);
297 throw new ITKCommsException("XML Error Processing ITK Response");
298 } catch (ParserConfigurationException pce) {
299 Logger.error("ParseConfigurationException on WS-CALL", pce);
300 throw new ITKCommsException("XML Configuration Error Processing ITK Response");
301 }
302
303 return responseDoc;
304
305 }
306
307
308
309
310
311
312
313
314 private String readInput(InputStream is) throws IOException {
315 BufferedInputStream bis = new BufferedInputStream(is);
316 byte[] contents = new byte[1024];
317 int bytesRead = 0;
318 String responseString = "";
319 while ((bytesRead = bis.read(contents)) != -1) {
320 responseString = responseString + new String(contents, 0, bytesRead);
321 }
322 Logger.trace("Response was:"+responseString);
323 bis.close();
324
325 return responseString ;
326 }
327 }