View Javadoc

1   /*
2      Licensed under the Apache License, Version 2.0 (the "License");
3      you may not use this file except in compliance with the License.
4      You may obtain a copy of the License at
5   
6        http://www.apache.org/licenses/LICENSE-2.0
7   
8      Unless required by applicable law or agreed to in writing, software
9      distributed under the License is distributed on an "AS IS" BASIS,
10     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11     See the License for the specific language governing permissions and
12     limitations under the License.
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 com.xmlsolutions.annotation.Requirement;
30  
31  import uk.nhs.interoperability.capabilities.AuditException;
32  import uk.nhs.interoperability.capabilities.AuditService;
33  import uk.nhs.interoperability.consumer.ITKMessageConsumer;
34  import uk.nhs.interoperability.infrastructure.ITKMessagePropertiesImpl;
35  import uk.nhs.interoperability.infrastructure.ITKMessagingException;
36  import uk.nhs.interoperability.infrastructure.ITKTransportPropertiesImpl;
37  import uk.nhs.interoperability.payload.ITKMessage;
38  import uk.nhs.interoperability.payload.SimpleMessage;
39  import uk.nhs.interoperability.service.ITKSimpleAudit;
40  import uk.nhs.interoperability.transport.ITKTransportProperties;
41  import uk.nhs.interoperability.transport.WS.ITKSOAPException;
42  import uk.nhs.interoperability.transport.WS.WSSOAPMessageImpl;
43  import uk.nhs.interoperability.util.Logger;
44  import uk.nhs.interoperability.util.ServletUtils;
45  import uk.nhs.interoperability.util.StringUtils;
46  import uk.nhs.interoperability.util.xml.DomUtils;
47  import uk.nhs.interoperability.util.xml.XPaths;
48  
49  /**
50   * The Class AbstractSimpleMessageServlet.
51   *
52   * @author Michael Odling-Smee
53   * @author Nicholas Jones
54   * @since 0.1
55   */
56  @SuppressWarnings("serial")
57  public abstract class AbstractSimpleMessageServlet extends ITKServlet {
58  	
59  	//Reference to the message consumer that will handle the request
60  	/** The message consumer. */
61  	private ITKMessageConsumer messageConsumer;
62  	
63  	/* (non-Javadoc)
64  	 * @see uk.nhs.interoperability.consumer.ITKServlet#init()
65  	 */
66  	@Override
67  	public void init() throws ServletException {
68  		this.messageConsumer = getMessageConsumer();
69  		super.init();
70  	}
71  	
72  	/**
73  	 * Gets the message consumer.
74  	 *
75  	 * @return the message consumer
76  	 */
77  	public abstract ITKMessageConsumer getMessageConsumer();
78  
79  	/* (non-Javadoc)
80  	 * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
81  	 */
82  	@Override
83  	@Requirement(traceTo={"WS-PAT-01"})
84  	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
85  		Logger.debug("Received a message"); 
86  		
87  		try {
88  			byte[] requestMsg = ServletUtils.readRequestContent(req);
89  			if (requestMsg == null) {
90  				throw new ITKMessagingException(ITKMessagingException.INVALID_MESSAGE_CODE, "The request did not contain any content");
91  			}
92  			String requestString = new String(requestMsg);
93  			ITKMessage responseMsg = this.processMessage(requestString);
94  			
95  			//If response message is null assume async invocation and reply with an empty HTTP 202
96  			if (responseMsg == null) {
97  				resp.setStatus(HttpServletResponse.SC_ACCEPTED);	
98  				Logger.trace("Replying HTTP 202");
99  				return;
100 			}
101 			Logger.trace("Response business payload:" + responseMsg.getFullPayload());
102 			
103 			//Otherwise send back the returned message having added the SOAP wrappers
104 			
105 			// No destination object is needed to respond on the same channel
106 			ITKMessage response = new WSSOAPMessageImpl(responseMsg, WSSOAPMessageImpl.SYNCRESP);
107 
108 			resp.getWriter().write(response.getFullPayload());
109 			return;
110 		} catch (ITKMessagingException e) {
111 			Logger.error("Could not process message", e);
112 			resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
113 			resp.getWriter().write(new ITKSOAPException(e).serialiseXML());
114 		} catch (Throwable t) {
115 			Logger.error("Could not process message - general error", t);
116 			resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
117 			resp.getWriter().write(t.getLocalizedMessage());
118 		} finally {
119 			Logger.debug("Completed processing");
120 		}
121 	}
122 	
123 	/**
124 	 * Process message.
125 	 *
126 	 * @param requestString the request string
127 	 * @return the iTK message
128 	 * @throws ITKMessagingException the iTK messaging exception
129 	 */
130 	@Requirement(traceTo={"WS-PAT-01"})
131 	private ITKMessage processMessage(String requestString) throws ITKMessagingException {	
132 		ITKTransportProperties itkTransportProperties = null;
133 		ITKMessagePropertiesImpl itkMessageProperties = null;
134 		try {
135 
136 			// 1) PROCESS THE REQUEST DOCUMENT
137 			//Parse the request
138 			Document doc = DomUtils.parse(requestString);
139 			//Pretty print the request message
140 			Logger.trace(DomUtils.serialiseToXML(doc, DomUtils.PRETTY_PRINT));
141 			
142 			// 2) PROCESS THE SOAP HEADERS
143 			//Extract the transport properties
144 			itkTransportProperties = ITKTransportPropertiesImpl.buildFromSoap(doc);
145 
146 			// 3) PROCESS THE DISTRIBUTION ENVELOPE
147 			//Extract the distribution envelope
148 			Document de = ITKMessagePropertiesImpl.extractDistributionEnvelopeFromSoap(doc);
149 			//Extract message properties from the request
150 			itkMessageProperties = (ITKMessagePropertiesImpl) ITKMessagePropertiesImpl.build(de);
151 			// Validate the message properties
152 			this.validateDistributionEnvelope(itkMessageProperties);
153 			//Attach the associated transport properties so that they are available for asynchronous invocations
154 			itkMessageProperties.setInboundTransportProperties(itkTransportProperties);
155 			
156 			// 4) PROCESS THE PAYLOAD
157 			//Obtain the actual business payload
158 			Document businessPayload = DomUtils.createDocumentFromNode((Node)XPaths.SOAP_WRAPPED_ITK_FIRST_PAYLOAD_XPATH.evaluate(doc, XPathConstants.NODE));
159 			String businessPayloadXML = DomUtils.serialiseToXML(businessPayload);
160 			//Show the extracted payload in the trace log
161 			Logger.trace(DomUtils.serialiseToXML(businessPayload, DomUtils.PRETTY_PRINT));
162 			
163 			// 5) HAND OFF TO THE MESSAGE HANDLER
164 			//Construct the appropriate object for handing over to the application
165 			
166 			ITKMessage requestMsg = new SimpleMessage(itkMessageProperties, businessPayloadXML);
167 			
168 			//Initialise the response
169 			ITKMessage response = null;
170 			//Determine which method to call on application
171 			if (StringUtils.hasValue(itkTransportProperties.getTransportReplyTo())) {
172 				/*
173 				 * ReplyTo is present - must want asynchronous response
174 				 * Use asynchronous application messaging interface
175 				 */
176 				this.messageConsumer.onMessage(requestMsg);
177 			} else {
178 				/*
179 				 * ReplyTo is not present assume synchronous application
180 				 * Invocation with business response
181 				 */
182 				response = this.messageConsumer.onSyncMessage(requestMsg);
183 			}
184 			//Audit receipt event
185 			ITKSimpleAudit.getInstance().auditEvent(AuditService.MESSAGE_RECEIPT_EVENT, System.currentTimeMillis(), itkMessageProperties);
186 			
187 			// 6) WRAP THE RESPONSE
188 			//Add any wrappers such as the distribution envelope as necessary
189 			response = this.addITKWrappers(response);
190 					
191 			return response;
192 		} catch (XPathExpressionException e) {
193 			throw new ITKMessagingException(itkTransportProperties, itkMessageProperties, ITKMessagingException.PROCESSING_ERROR_NOT_RETRYABLE_CODE, "Could not extract values from request", e);
194 		} catch (IOException e) {
195 			throw new ITKMessagingException(itkTransportProperties, itkMessageProperties, ITKMessagingException.PROCESSING_ERROR_NOT_RETRYABLE_CODE, "Could not parse request", e);
196 		} catch (ParserConfigurationException e) {
197 			throw new ITKMessagingException(itkTransportProperties, itkMessageProperties, ITKMessagingException.PROCESSING_ERROR_NOT_RETRYABLE_CODE, "XML parser configuration error", e);
198 		} catch (AuditException e) {
199 			throw new ITKMessagingException(itkTransportProperties, itkMessageProperties, ITKMessagingException.PROCESSING_ERROR_RETRYABLE_CODE, "Failed to write audit", e);
200 		} catch (SAXException e) {
201 			throw new ITKMessagingException(itkTransportProperties, itkMessageProperties, ITKMessagingException.PROCESSING_ERROR_NOT_RETRYABLE_CODE, "Error parsing request XML", e);
202 		}
203 	}	
204 
205 }