1   /*
2    * Copyright 2002-2009 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.ws.soap.server.endpoint.interceptor;
18  
19  import java.io.ByteArrayOutputStream;
20  import java.io.InputStream;
21  import java.util.Locale;
22  import javax.xml.XMLConstants;
23  import javax.xml.soap.MessageFactory;
24  import javax.xml.soap.SOAPConstants;
25  import javax.xml.soap.SOAPMessage;
26  import javax.xml.transform.Transformer;
27  import javax.xml.transform.TransformerFactory;
28  import javax.xml.transform.stream.StreamSource;
29  
30  import org.custommonkey.xmlunit.XMLTestCase;
31  import org.xml.sax.SAXParseException;
32  import org.xml.sax.helpers.LocatorImpl;
33  
34  import org.springframework.core.io.ClassPathResource;
35  import org.springframework.core.io.Resource;
36  import org.springframework.ws.MockWebServiceMessage;
37  import org.springframework.ws.MockWebServiceMessageFactory;
38  import org.springframework.ws.WebServiceMessage;
39  import org.springframework.ws.context.DefaultMessageContext;
40  import org.springframework.ws.context.MessageContext;
41  import org.springframework.ws.soap.SoapMessage;
42  import org.springframework.ws.soap.SoapVersion;
43  import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory;
44  import org.springframework.ws.soap.saaj.SaajSoapMessage;
45  import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
46  import org.springframework.ws.soap.saaj.support.SaajUtils;
47  import org.springframework.ws.soap.soap11.Soap11Fault;
48  import org.springframework.ws.soap.soap12.Soap12Fault;
49  import org.springframework.ws.transport.MockTransportInputStream;
50  import org.springframework.ws.transport.TransportInputStream;
51  import org.springframework.xml.xsd.SimpleXsdSchema;
52  
53  public class PayloadValidatingInterceptorTest extends XMLTestCase {
54  
55      private PayloadValidatingInterceptor interceptor;
56  
57      private MessageContext context;
58  
59      private SaajSoapMessageFactory soap11Factory;
60  
61      private SaajSoapMessageFactory soap12Factory;
62  
63      private Transformer transformer;
64  
65      private static final String INVALID_MESSAGE = "invalidMessage.xml";
66  
67      private static final String SCHEMA = "schema.xsd";
68  
69      private static final String VALID_MESSAGE = "validMessage.xml";
70  
71      private static final String PRODUCT_SCHEMA = "productSchema.xsd";
72  
73      private static final String SIZE_SCHEMA = "sizeSchema.xsd";
74  
75      private static final String VALID_SOAP_MESSAGE = "validSoapMessage.xml";
76  
77      private static final String SCHEMA2 = "schema2.xsd";
78  
79      protected void setUp() throws Exception {
80          interceptor = new PayloadValidatingInterceptor();
81          interceptor.setSchema(new ClassPathResource(SCHEMA, getClass()));
82          interceptor.setValidateRequest(true);
83          interceptor.setValidateResponse(true);
84          interceptor.afterPropertiesSet();
85  
86          soap11Factory = new SaajSoapMessageFactory(MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL));
87          soap12Factory = new SaajSoapMessageFactory(MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL));
88          transformer = TransformerFactory.newInstance().newTransformer();
89      }
90  
91      public void testHandleInvalidRequestSoap11() throws Exception {
92          SoapMessage invalidMessage = (SoapMessage) soap11Factory.createWebServiceMessage();
93          InputStream inputStream = getClass().getResourceAsStream(INVALID_MESSAGE);
94          transformer.transform(new StreamSource(inputStream), invalidMessage.getPayloadResult());
95          context = new DefaultMessageContext(invalidMessage, soap11Factory);
96  
97          boolean result = interceptor.handleRequest(context, null);
98          assertFalse("Invalid response from interceptor", result);
99          assertTrue("Context has no response", context.hasResponse());
100         SoapMessage response = (SoapMessage) context.getResponse();
101         assertTrue("Response has no fault", response.getSoapBody().hasFault());
102         Soap11Fault fault = (Soap11Fault) response.getSoapBody().getFault();
103         assertEquals("Invalid fault code on fault", SoapVersion.SOAP_11.getClientOrSenderFaultName(),
104                 fault.getFaultCode());
105         assertEquals("Invalid fault string on fault", PayloadValidatingInterceptor.DEFAULT_FAULTSTRING_OR_REASON,
106                 fault.getFaultStringOrReason());
107         assertNotNull("No Detail on fault", fault.getFaultDetail());
108     }
109 
110     public void testHandleInvalidRequestSoap12() throws Exception {
111         SoapMessage invalidMessage = (SoapMessage) soap12Factory.createWebServiceMessage();
112         InputStream inputStream = getClass().getResourceAsStream(INVALID_MESSAGE);
113         transformer.transform(new StreamSource(inputStream), invalidMessage.getPayloadResult());
114         context = new DefaultMessageContext(invalidMessage, soap12Factory);
115 
116         boolean result = interceptor.handleRequest(context, null);
117         assertFalse("Invalid response from interceptor", result);
118         assertTrue("Context has no response", context.hasResponse());
119         SoapMessage response = (SoapMessage) context.getResponse();
120         assertTrue("Response has no fault", response.getSoapBody().hasFault());
121         Soap12Fault fault = (Soap12Fault) response.getSoapBody().getFault();
122         assertEquals("Invalid fault code on fault", SoapVersion.SOAP_12.getClientOrSenderFaultName(),
123                 fault.getFaultCode());
124         assertEquals("Invalid fault string on fault", PayloadValidatingInterceptor.DEFAULT_FAULTSTRING_OR_REASON,
125                 fault.getFaultReasonText(Locale.ENGLISH));
126         assertNotNull("No Detail on fault", fault.getFaultDetail());
127     }
128 
129     public void testHandleInvalidRequestOverridenProperties() throws Exception {
130         String faultString = "fout";
131         Locale locale = new Locale("nl");
132         interceptor.setFaultStringOrReason(faultString);
133         interceptor.setFaultStringOrReasonLocale(locale);
134         interceptor.setAddValidationErrorDetail(false);
135 
136         SoapMessage invalidMessage = (SoapMessage) soap11Factory.createWebServiceMessage();
137         InputStream inputStream = getClass().getResourceAsStream(INVALID_MESSAGE);
138         transformer.transform(new StreamSource(inputStream), invalidMessage.getPayloadResult());
139         context = new DefaultMessageContext(invalidMessage, soap11Factory);
140 
141         boolean result = interceptor.handleRequest(context, null);
142         assertFalse("Invalid response from interceptor", result);
143         assertTrue("Context has no response", context.hasResponse());
144         SoapMessage response = (SoapMessage) context.getResponse();
145         assertTrue("Response has no fault", response.getSoapBody().hasFault());
146         Soap11Fault fault = (Soap11Fault) response.getSoapBody().getFault();
147         assertEquals("Invalid fault code on fault", SoapVersion.SOAP_11.getClientOrSenderFaultName(),
148                 fault.getFaultCode());
149         assertEquals("Invalid fault string on fault", faultString, fault.getFaultStringOrReason());
150         assertEquals("Invalid fault string locale on fault", locale, fault.getFaultStringLocale());
151         assertNull("Detail on fault", fault.getFaultDetail());
152     }
153 
154     public void testHandlerInvalidRequest() throws Exception {
155         MockWebServiceMessage request = new MockWebServiceMessage();
156         request.setPayload(new ClassPathResource(INVALID_MESSAGE, getClass()));
157         context = new DefaultMessageContext(request, new MockWebServiceMessageFactory());
158         boolean result = interceptor.handleRequest(context, null);
159         assertFalse("Invalid response from interceptor", result);
160     }
161 
162     public void testHandleValidRequest() throws Exception {
163         MockWebServiceMessage request = new MockWebServiceMessage();
164         request.setPayload(new ClassPathResource(VALID_MESSAGE, getClass()));
165         context = new DefaultMessageContext(request, new MockWebServiceMessageFactory());
166         boolean result = interceptor.handleRequest(context, null);
167         assertTrue("Invalid response from interceptor", result);
168         assertFalse("Response set", context.hasResponse());
169     }
170 
171     public void testHandleInvalidResponse() throws Exception {
172         MockWebServiceMessage request = new MockWebServiceMessage();
173         context = new DefaultMessageContext(request, new MockWebServiceMessageFactory());
174         MockWebServiceMessage response = (MockWebServiceMessage) context.getResponse();
175         response.setPayload(new ClassPathResource(INVALID_MESSAGE, getClass()));
176         boolean result = interceptor.handleResponse(context, null);
177         assertFalse("Invalid response from interceptor", result);
178     }
179 
180     public void testHandleValidResponse() throws Exception {
181         MockWebServiceMessage request = new MockWebServiceMessage();
182         context = new DefaultMessageContext(request, new MockWebServiceMessageFactory());
183         MockWebServiceMessage response = (MockWebServiceMessage) context.getResponse();
184         response.setPayload(new ClassPathResource(VALID_MESSAGE, getClass()));
185         boolean result = interceptor.handleResponse(context, null);
186         assertTrue("Invalid response from interceptor", result);
187     }
188 
189     public void testNamespacesInType() throws Exception {
190         // Make sure we use Xerces for this testcase: the JAXP implementation used internally by JDK 1.5 has a bug
191         // See http://opensource.atlassian.com/projects/spring/browse/SWS-35
192         String previousSchemaFactory =
193                 System.getProperty("javax.xml.validation.SchemaFactory:" + XMLConstants.W3C_XML_SCHEMA_NS_URI, "");
194         System.setProperty("javax.xml.validation.SchemaFactory:" + XMLConstants.W3C_XML_SCHEMA_NS_URI,
195                 "org.apache.xerces.jaxp.validation.XMLSchemaFactory");
196         try {
197             PayloadValidatingInterceptor interceptor = new PayloadValidatingInterceptor();
198             interceptor.setSchema(new ClassPathResource(SCHEMA2, PayloadValidatingInterceptorTest.class));
199             interceptor.afterPropertiesSet();
200             MessageFactory messageFactory = MessageFactory.newInstance();
201             SOAPMessage saajMessage =
202                     SaajUtils.loadMessage(new ClassPathResource(VALID_SOAP_MESSAGE, getClass()), messageFactory);
203             context = new DefaultMessageContext(new SaajSoapMessage(saajMessage),
204                     new SaajSoapMessageFactory(messageFactory));
205 
206             boolean result = interceptor.handleRequest(context, null);
207             assertTrue("Invalid response from interceptor", result);
208             assertFalse("Response set", context.hasResponse());
209         }
210         finally {
211             // Reset the property
212             System.setProperty("javax.xml.validation.SchemaFactory:" + XMLConstants.W3C_XML_SCHEMA_NS_URI,
213                     previousSchemaFactory);
214         }
215     }
216 
217     public void testNonExistingSchema() throws Exception {
218         try {
219             interceptor.setSchema(new ClassPathResource("invalid"));
220             interceptor.afterPropertiesSet();
221             fail("IllegalArgumentException expected");
222         }
223         catch (IllegalArgumentException ex) {
224             // expected
225         }
226     }
227 
228     public void testHandlerInvalidRequestMultipleSchemas() throws Exception {
229         interceptor.setSchemas(new Resource[]{new ClassPathResource(PRODUCT_SCHEMA, getClass()),
230                 new ClassPathResource(SIZE_SCHEMA, getClass())});
231         interceptor.afterPropertiesSet();
232         MockWebServiceMessage request = new MockWebServiceMessage(new ClassPathResource(INVALID_MESSAGE, getClass()));
233         context = new DefaultMessageContext(request, new MockWebServiceMessageFactory());
234         boolean result = interceptor.handleRequest(context, null);
235         assertFalse("Invalid response from interceptor", result);
236     }
237 
238     public void testHandleValidRequestMultipleSchemas() throws Exception {
239         interceptor.setSchemas(new Resource[]{new ClassPathResource(PRODUCT_SCHEMA, getClass()),
240                 new ClassPathResource(SIZE_SCHEMA, getClass())});
241         interceptor.afterPropertiesSet();
242         MockWebServiceMessage request = new MockWebServiceMessage(new ClassPathResource(VALID_MESSAGE, getClass()));
243         context = new DefaultMessageContext(request, new MockWebServiceMessageFactory());
244 
245         boolean result = interceptor.handleRequest(context, null);
246         assertTrue("Invalid response from interceptor", result);
247         assertFalse("Response set", context.hasResponse());
248     }
249 
250     public void testHandleInvalidResponseMultipleSchemas() throws Exception {
251         interceptor.setSchemas(new Resource[]{new ClassPathResource(PRODUCT_SCHEMA, getClass()),
252                 new ClassPathResource(SIZE_SCHEMA, getClass())});
253         interceptor.afterPropertiesSet();
254         MockWebServiceMessage request = new MockWebServiceMessage();
255         context = new DefaultMessageContext(request, new MockWebServiceMessageFactory());
256         MockWebServiceMessage response = (MockWebServiceMessage) context.getResponse();
257         response.setPayload(new ClassPathResource(INVALID_MESSAGE, getClass()));
258         boolean result = interceptor.handleResponse(context, null);
259         assertFalse("Invalid response from interceptor", result);
260     }
261 
262     public void testHandleValidResponseMultipleSchemas() throws Exception {
263         interceptor.setSchemas(new Resource[]{new ClassPathResource(PRODUCT_SCHEMA, getClass()),
264                 new ClassPathResource(SIZE_SCHEMA, getClass())});
265         interceptor.afterPropertiesSet();
266         MockWebServiceMessage request = new MockWebServiceMessage();
267         context = new DefaultMessageContext(request, new MockWebServiceMessageFactory());
268         MockWebServiceMessage response = (MockWebServiceMessage) context.getResponse();
269         response.setPayload(new ClassPathResource(VALID_MESSAGE, getClass()));
270         boolean result = interceptor.handleResponse(context, null);
271         assertTrue("Invalid response from interceptor", result);
272     }
273 
274     public void testCreateRequestValidationFaultAxiom() throws Exception {
275         LocatorImpl locator = new LocatorImpl();
276         locator.setLineNumber(0);
277         locator.setColumnNumber(0);
278         SAXParseException[] exceptions = new SAXParseException[]{new SAXParseException("Message 1", locator),
279                 new SAXParseException("Message 2", locator),};
280         MessageContext messageContext = new DefaultMessageContext(new AxiomSoapMessageFactory());
281         interceptor.handleRequestValidationErrors(messageContext, exceptions);
282         ByteArrayOutputStream os = new ByteArrayOutputStream();
283         messageContext.getResponse().writeTo(os);
284         assertXMLEqual(
285                 "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">" + "<soapenv:Body>" +
286                         "<soapenv:Fault>" + "<faultcode>soapenv:Client</faultcode>" +
287                         "<faultstring xml:lang='en'>Validation error</faultstring>" + "<detail>" +
288                         "<spring-ws:ValidationError xmlns:spring-ws=\"http://springframework.org/spring-ws\">Message 1</spring-ws:ValidationError>" +
289                         "<spring-ws:ValidationError xmlns:spring-ws=\"http://springframework.org/spring-ws\">Message 2</spring-ws:ValidationError>" +
290                         "</detail>" + "</soapenv:Fault>" + "</soapenv:Body>" + "</soapenv:Envelope>", os.toString());
291 
292     }
293 
294     public void testXsdSchema() throws Exception {
295         PayloadValidatingInterceptor interceptor = new PayloadValidatingInterceptor();
296         SimpleXsdSchema schema = new SimpleXsdSchema(new ClassPathResource(SCHEMA, getClass()));
297         schema.afterPropertiesSet();
298         interceptor.setXsdSchema(schema);
299         interceptor.setValidateRequest(true);
300         interceptor.setValidateResponse(true);
301         interceptor.afterPropertiesSet();
302         MockWebServiceMessage request = new MockWebServiceMessage();
303         request.setPayload(new ClassPathResource(VALID_MESSAGE, getClass()));
304         context = new DefaultMessageContext(request, new MockWebServiceMessageFactory());
305         boolean result = interceptor.handleRequest(context, null);
306         assertTrue("Invalid response from interceptor", result);
307         assertFalse("Response set", context.hasResponse());
308     }
309 
310     public void testAxiom() throws Exception {
311         AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
312         messageFactory.setPayloadCaching(true);
313         messageFactory.afterPropertiesSet();
314 
315         PayloadValidatingInterceptor interceptor = new PayloadValidatingInterceptor();
316         interceptor.setSchema(new ClassPathResource("codexws.xsd", getClass()));
317         interceptor.afterPropertiesSet();
318 
319         Resource resource = new ClassPathResource("axiom.xml", getClass());
320         TransportInputStream tis = new MockTransportInputStream(resource.getInputStream());
321         WebServiceMessage message = messageFactory.createWebServiceMessage(tis);
322         MessageContext context = new DefaultMessageContext(message, messageFactory);
323         boolean result = interceptor.handleRequest(context, null);
324         assertTrue("Invalid response from interceptor", result);
325 
326     }
327 
328     public void testMultipleNamespacesAxiom() throws Exception {
329         AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
330         messageFactory.setPayloadCaching(true);
331         messageFactory.afterPropertiesSet();
332 
333         PayloadValidatingInterceptor interceptor = new PayloadValidatingInterceptor();
334         interceptor.setSchema(new ClassPathResource("multipleNamespaces.xsd", getClass()));
335         interceptor.afterPropertiesSet();
336 
337         Resource resource = new ClassPathResource("multipleNamespaces.xml", getClass());
338         TransportInputStream tis = new MockTransportInputStream(resource.getInputStream());
339         WebServiceMessage message = messageFactory.createWebServiceMessage(tis);
340         MessageContext context = new DefaultMessageContext(message, messageFactory);
341         boolean result = interceptor.handleRequest(context, null);
342         assertTrue("Invalid response from interceptor", result);
343 
344     }
345 
346 }