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 }