1   /*
2    * Copyright 2005-2011 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.springframework.core.io.ClassPathResource;
31  import org.springframework.core.io.Resource;
32  import org.springframework.ws.MockWebServiceMessage;
33  import org.springframework.ws.MockWebServiceMessageFactory;
34  import org.springframework.ws.WebServiceMessage;
35  import org.springframework.ws.context.DefaultMessageContext;
36  import org.springframework.ws.context.MessageContext;
37  import org.springframework.ws.soap.SoapMessage;
38  import org.springframework.ws.soap.SoapVersion;
39  import org.springframework.ws.soap.axiom.AxiomSoapMessageFactory;
40  import org.springframework.ws.soap.saaj.SaajSoapMessage;
41  import org.springframework.ws.soap.saaj.SaajSoapMessageFactory;
42  import org.springframework.ws.soap.saaj.support.SaajUtils;
43  import org.springframework.ws.soap.soap11.Soap11Fault;
44  import org.springframework.ws.soap.soap12.Soap12Fault;
45  import org.springframework.ws.transport.MockTransportInputStream;
46  import org.springframework.ws.transport.TransportInputStream;
47  import org.springframework.xml.validation.ValidationErrorHandler;
48  import org.springframework.xml.xsd.SimpleXsdSchema;
49  
50  import org.junit.Assert;
51  import org.junit.Before;
52  import org.junit.Test;
53  import org.xml.sax.SAXException;
54  import org.xml.sax.SAXParseException;
55  import org.xml.sax.helpers.LocatorImpl;
56  
57  import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual;
58  
59  public class PayloadValidatingInterceptorTest {
60  
61      private PayloadValidatingInterceptor interceptor;
62  
63      private MessageContext context;
64  
65      private SaajSoapMessageFactory soap11Factory;
66  
67      private SaajSoapMessageFactory soap12Factory;
68  
69      private Transformer transformer;
70  
71      private static final String INVALID_MESSAGE = "invalidMessage.xml";
72  
73      private static final String SCHEMA = "schema.xsd";
74  
75      private static final String VALID_MESSAGE = "validMessage.xml";
76  
77      private static final String PRODUCT_SCHEMA = "productSchema.xsd";
78  
79      private static final String SIZE_SCHEMA = "sizeSchema.xsd";
80  
81      private static final String VALID_SOAP_MESSAGE = "validSoapMessage.xml";
82  
83      private static final String SCHEMA2 = "schema2.xsd";
84  
85      @Before
86      public void setUp() throws Exception {
87          interceptor = new PayloadValidatingInterceptor();
88          interceptor.setSchema(new ClassPathResource(SCHEMA, getClass()));
89          interceptor.setValidateRequest(true);
90          interceptor.setValidateResponse(true);
91          interceptor.afterPropertiesSet();
92  
93          soap11Factory = new SaajSoapMessageFactory(MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL));
94          soap12Factory = new SaajSoapMessageFactory(MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL));
95          transformer = TransformerFactory.newInstance().newTransformer();
96      }
97  
98      @Test
99      public void testHandleInvalidRequestSoap11() throws Exception {
100         SoapMessage invalidMessage = soap11Factory.createWebServiceMessage();
101         InputStream inputStream = getClass().getResourceAsStream(INVALID_MESSAGE);
102         transformer.transform(new StreamSource(inputStream), invalidMessage.getPayloadResult());
103         context = new DefaultMessageContext(invalidMessage, soap11Factory);
104 
105         boolean result = interceptor.handleRequest(context, null);
106         Assert.assertFalse("Invalid response from interceptor", result);
107         Assert.assertTrue("Context has no response", context.hasResponse());
108         SoapMessage response = (SoapMessage) context.getResponse();
109         Assert.assertTrue("Response has no fault", response.getSoapBody().hasFault());
110         Soap11Fault fault = (Soap11Fault) response.getSoapBody().getFault();
111         Assert.assertEquals("Invalid fault code on fault", SoapVersion.SOAP_11.getClientOrSenderFaultName(),
112                 fault.getFaultCode());
113         Assert.assertEquals("Invalid fault string on fault", PayloadValidatingInterceptor.DEFAULT_FAULTSTRING_OR_REASON,
114                 fault.getFaultStringOrReason());
115         Assert.assertNotNull("No Detail on fault", fault.getFaultDetail());
116     }
117 
118     @Test
119     public void testHandleInvalidRequestSoap12() throws Exception {
120         SoapMessage invalidMessage = soap12Factory.createWebServiceMessage();
121         InputStream inputStream = getClass().getResourceAsStream(INVALID_MESSAGE);
122         transformer.transform(new StreamSource(inputStream), invalidMessage.getPayloadResult());
123         context = new DefaultMessageContext(invalidMessage, soap12Factory);
124 
125         boolean result = interceptor.handleRequest(context, null);
126         Assert.assertFalse("Invalid response from interceptor", result);
127         Assert.assertTrue("Context has no response", context.hasResponse());
128         SoapMessage response = (SoapMessage) context.getResponse();
129         Assert.assertTrue("Response has no fault", response.getSoapBody().hasFault());
130         Soap12Fault fault = (Soap12Fault) response.getSoapBody().getFault();
131         Assert.assertEquals("Invalid fault code on fault", SoapVersion.SOAP_12.getClientOrSenderFaultName(),
132                 fault.getFaultCode());
133         Assert.assertEquals("Invalid fault string on fault", PayloadValidatingInterceptor.DEFAULT_FAULTSTRING_OR_REASON,
134                 fault.getFaultReasonText(Locale.ENGLISH));
135         Assert.assertNotNull("No Detail on fault", fault.getFaultDetail());
136     }
137 
138     @Test
139     public void testHandleInvalidRequestOverridenProperties() throws Exception {
140         String faultString = "fout";
141         Locale locale = new Locale("nl");
142         interceptor.setFaultStringOrReason(faultString);
143         interceptor.setFaultStringOrReasonLocale(locale);
144         interceptor.setAddValidationErrorDetail(false);
145 
146         SoapMessage invalidMessage = (SoapMessage) soap11Factory.createWebServiceMessage();
147         InputStream inputStream = getClass().getResourceAsStream(INVALID_MESSAGE);
148         transformer.transform(new StreamSource(inputStream), invalidMessage.getPayloadResult());
149         context = new DefaultMessageContext(invalidMessage, soap11Factory);
150 
151         boolean result = interceptor.handleRequest(context, null);
152         Assert.assertFalse("Invalid response from interceptor", result);
153         Assert.assertTrue("Context has no response", context.hasResponse());
154         SoapMessage response = (SoapMessage) context.getResponse();
155         Assert.assertTrue("Response has no fault", response.getSoapBody().hasFault());
156         Soap11Fault fault = (Soap11Fault) response.getSoapBody().getFault();
157         Assert.assertEquals("Invalid fault code on fault", SoapVersion.SOAP_11.getClientOrSenderFaultName(),
158                 fault.getFaultCode());
159         Assert.assertEquals("Invalid fault string on fault", faultString, fault.getFaultStringOrReason());
160         Assert.assertEquals("Invalid fault string locale on fault", locale, fault.getFaultStringLocale());
161         Assert.assertNull("Detail on fault", fault.getFaultDetail());
162     }
163 
164     @Test
165     public void testHandlerInvalidRequest() throws Exception {
166         MockWebServiceMessage request = new MockWebServiceMessage();
167         request.setPayload(new ClassPathResource(INVALID_MESSAGE, getClass()));
168         context = new DefaultMessageContext(request, new MockWebServiceMessageFactory());
169         boolean result = interceptor.handleRequest(context, null);
170         Assert.assertFalse("Invalid response from interceptor", result);
171     }
172 
173     @Test
174     public void testHandleValidRequest() throws Exception {
175         MockWebServiceMessage request = new MockWebServiceMessage();
176         request.setPayload(new ClassPathResource(VALID_MESSAGE, getClass()));
177         context = new DefaultMessageContext(request, new MockWebServiceMessageFactory());
178         boolean result = interceptor.handleRequest(context, null);
179         Assert.assertTrue("Invalid response from interceptor", result);
180         Assert.assertFalse("Response set", context.hasResponse());
181     }
182 
183     @Test
184     public void testHandleInvalidResponse() throws Exception {
185         MockWebServiceMessage request = new MockWebServiceMessage();
186         context = new DefaultMessageContext(request, new MockWebServiceMessageFactory());
187         MockWebServiceMessage response = (MockWebServiceMessage) context.getResponse();
188         response.setPayload(new ClassPathResource(INVALID_MESSAGE, getClass()));
189         boolean result = interceptor.handleResponse(context, null);
190         Assert.assertFalse("Invalid response from interceptor", result);
191     }
192 
193     @Test
194     public void testHandleValidResponse() throws Exception {
195         MockWebServiceMessage request = new MockWebServiceMessage();
196         context = new DefaultMessageContext(request, new MockWebServiceMessageFactory());
197         MockWebServiceMessage response = (MockWebServiceMessage) context.getResponse();
198         response.setPayload(new ClassPathResource(VALID_MESSAGE, getClass()));
199         boolean result = interceptor.handleResponse(context, null);
200         Assert.assertTrue("Invalid response from interceptor", result);
201     }
202 
203     @Test
204     public void testNamespacesInType() throws Exception {
205         // Make sure we use Xerces for this testcase: the JAXP implementation used internally by JDK 1.5 has a bug
206         // See http://opensource.atlassian.com/projects/spring/browse/SWS-35
207         String previousSchemaFactory =
208                 System.getProperty("javax.xml.validation.SchemaFactory:" + XMLConstants.W3C_XML_SCHEMA_NS_URI, "");
209         System.setProperty("javax.xml.validation.SchemaFactory:" + XMLConstants.W3C_XML_SCHEMA_NS_URI,
210                 "org.apache.xerces.jaxp.validation.XMLSchemaFactory");
211         try {
212             PayloadValidatingInterceptor interceptor = new PayloadValidatingInterceptor();
213             interceptor.setSchema(new ClassPathResource(SCHEMA2, PayloadValidatingInterceptorTest.class));
214             interceptor.afterPropertiesSet();
215             MessageFactory messageFactory = MessageFactory.newInstance();
216             SOAPMessage saajMessage =
217                     SaajUtils.loadMessage(new ClassPathResource(VALID_SOAP_MESSAGE, getClass()), messageFactory);
218             context = new DefaultMessageContext(new SaajSoapMessage(saajMessage),
219                     new SaajSoapMessageFactory(messageFactory));
220 
221             boolean result = interceptor.handleRequest(context, null);
222             Assert.assertTrue("Invalid response from interceptor", result);
223             Assert.assertFalse("Response set", context.hasResponse());
224         }
225         finally {
226             // Reset the property
227             System.setProperty("javax.xml.validation.SchemaFactory:" + XMLConstants.W3C_XML_SCHEMA_NS_URI,
228                     previousSchemaFactory);
229         }
230     }
231 
232     @Test
233     public void testNonExistingSchema() throws Exception {
234         try {
235             interceptor.setSchema(new ClassPathResource("invalid"));
236             interceptor.afterPropertiesSet();
237             Assert.fail("IllegalArgumentException expected");
238         }
239         catch (IllegalArgumentException ex) {
240             // expected
241         }
242     }
243 
244     @Test
245     public void testHandlerInvalidRequestMultipleSchemas() throws Exception {
246         interceptor.setSchemas(new Resource[]{new ClassPathResource(PRODUCT_SCHEMA, getClass()),
247                 new ClassPathResource(SIZE_SCHEMA, getClass())});
248         interceptor.afterPropertiesSet();
249         MockWebServiceMessage request = new MockWebServiceMessage(new ClassPathResource(INVALID_MESSAGE, getClass()));
250         context = new DefaultMessageContext(request, new MockWebServiceMessageFactory());
251         boolean result = interceptor.handleRequest(context, null);
252         Assert.assertFalse("Invalid response from interceptor", result);
253     }
254 
255     @Test
256     public void testHandleValidRequestMultipleSchemas() throws Exception {
257         interceptor.setSchemas(new Resource[]{new ClassPathResource(PRODUCT_SCHEMA, getClass()),
258                 new ClassPathResource(SIZE_SCHEMA, getClass())});
259         interceptor.afterPropertiesSet();
260         MockWebServiceMessage request = new MockWebServiceMessage(new ClassPathResource(VALID_MESSAGE, getClass()));
261         context = new DefaultMessageContext(request, new MockWebServiceMessageFactory());
262 
263         boolean result = interceptor.handleRequest(context, null);
264         Assert.assertTrue("Invalid response from interceptor", result);
265         Assert.assertFalse("Response set", context.hasResponse());
266     }
267 
268     @Test
269     public void testHandleInvalidResponseMultipleSchemas() throws Exception {
270         interceptor.setSchemas(new Resource[]{new ClassPathResource(PRODUCT_SCHEMA, getClass()),
271                 new ClassPathResource(SIZE_SCHEMA, getClass())});
272         interceptor.afterPropertiesSet();
273         MockWebServiceMessage request = new MockWebServiceMessage();
274         context = new DefaultMessageContext(request, new MockWebServiceMessageFactory());
275         MockWebServiceMessage response = (MockWebServiceMessage) context.getResponse();
276         response.setPayload(new ClassPathResource(INVALID_MESSAGE, getClass()));
277         boolean result = interceptor.handleResponse(context, null);
278         Assert.assertFalse("Invalid response from interceptor", result);
279     }
280 
281     @Test
282     public void testHandleValidResponseMultipleSchemas() throws Exception {
283         interceptor.setSchemas(new Resource[]{new ClassPathResource(PRODUCT_SCHEMA, getClass()),
284                 new ClassPathResource(SIZE_SCHEMA, getClass())});
285         interceptor.afterPropertiesSet();
286         MockWebServiceMessage request = new MockWebServiceMessage();
287         context = new DefaultMessageContext(request, new MockWebServiceMessageFactory());
288         MockWebServiceMessage response = (MockWebServiceMessage) context.getResponse();
289         response.setPayload(new ClassPathResource(VALID_MESSAGE, getClass()));
290         boolean result = interceptor.handleResponse(context, null);
291         Assert.assertTrue("Invalid response from interceptor", result);
292     }
293 
294     @Test
295     public void testCreateRequestValidationFaultAxiom() throws Exception {
296         LocatorImpl locator = new LocatorImpl();
297         locator.setLineNumber(0);
298         locator.setColumnNumber(0);
299         SAXParseException[] exceptions = new SAXParseException[]{new SAXParseException("Message 1", locator),
300                 new SAXParseException("Message 2", locator),};
301         MessageContext messageContext = new DefaultMessageContext(new AxiomSoapMessageFactory());
302         interceptor.handleRequestValidationErrors(messageContext, exceptions);
303         ByteArrayOutputStream os = new ByteArrayOutputStream();
304         messageContext.getResponse().writeTo(os);
305         assertXMLEqual(
306                 "<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">" + "<soapenv:Body>" +
307                         "<soapenv:Fault>" + "<faultcode>soapenv:Client</faultcode>" +
308                         "<faultstring xml:lang='en'>Validation error</faultstring>" + "<detail>" +
309                         "<spring-ws:ValidationError xmlns:spring-ws=\"http://springframework.org/spring-ws\">Message 1</spring-ws:ValidationError>" +
310                         "<spring-ws:ValidationError xmlns:spring-ws=\"http://springframework.org/spring-ws\">Message 2</spring-ws:ValidationError>" +
311                         "</detail>" + "</soapenv:Fault>" + "</soapenv:Body>" + "</soapenv:Envelope>", os.toString());
312 
313     }
314 
315     @Test
316     public void testXsdSchema() throws Exception {
317         PayloadValidatingInterceptor interceptor = new PayloadValidatingInterceptor();
318         SimpleXsdSchema schema = new SimpleXsdSchema(new ClassPathResource(SCHEMA, getClass()));
319         schema.afterPropertiesSet();
320         interceptor.setXsdSchema(schema);
321         interceptor.setValidateRequest(true);
322         interceptor.setValidateResponse(true);
323         interceptor.afterPropertiesSet();
324         MockWebServiceMessage request = new MockWebServiceMessage();
325         request.setPayload(new ClassPathResource(VALID_MESSAGE, getClass()));
326         context = new DefaultMessageContext(request, new MockWebServiceMessageFactory());
327         boolean result = interceptor.handleRequest(context, null);
328         Assert.assertTrue("Invalid response from interceptor", result);
329         Assert.assertFalse("Response set", context.hasResponse());
330     }
331 
332     @Test
333     public void testAxiom() throws Exception {
334         AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
335         messageFactory.setPayloadCaching(true);
336         messageFactory.afterPropertiesSet();
337 
338         PayloadValidatingInterceptor interceptor = new PayloadValidatingInterceptor();
339         interceptor.setSchema(new ClassPathResource("codexws.xsd", getClass()));
340         interceptor.afterPropertiesSet();
341 
342         Resource resource = new ClassPathResource("axiom.xml", getClass());
343         TransportInputStream tis = new MockTransportInputStream(resource.getInputStream());
344         WebServiceMessage message = messageFactory.createWebServiceMessage(tis);
345         MessageContext context = new DefaultMessageContext(message, messageFactory);
346         boolean result = interceptor.handleRequest(context, null);
347         Assert.assertTrue("Invalid response from interceptor", result);
348 
349     }
350 
351     @Test
352     public void testMultipleNamespacesAxiom() throws Exception {
353         AxiomSoapMessageFactory messageFactory = new AxiomSoapMessageFactory();
354         messageFactory.setPayloadCaching(true);
355         messageFactory.afterPropertiesSet();
356 
357         PayloadValidatingInterceptor interceptor = new PayloadValidatingInterceptor();
358         interceptor.setSchema(new ClassPathResource("multipleNamespaces.xsd", getClass()));
359         interceptor.afterPropertiesSet();
360 
361         Resource resource = new ClassPathResource("multipleNamespaces.xml", getClass());
362         TransportInputStream tis = new MockTransportInputStream(resource.getInputStream());
363         WebServiceMessage message = messageFactory.createWebServiceMessage(tis);
364         MessageContext context = new DefaultMessageContext(message, messageFactory);
365         boolean result = interceptor.handleRequest(context, null);
366         Assert.assertTrue("Invalid response from interceptor", result);
367 
368     }
369 
370     @Test
371     public void customErrorHandler() throws Exception {
372         ValidationErrorHandler errorHandler = new ValidationErrorHandler() {
373             public SAXParseException[] getErrors() {
374                 return new SAXParseException[0];
375             }
376 
377             public void warning(SAXParseException exception) throws SAXException {
378             }
379 
380             public void error(SAXParseException exception) throws SAXException {
381             }
382 
383             public void fatalError(SAXParseException exception) throws SAXException {
384             }
385         };
386         interceptor.setErrorHandler(errorHandler);
387         SoapMessage invalidMessage = soap11Factory.createWebServiceMessage();
388         InputStream inputStream = getClass().getResourceAsStream(INVALID_MESSAGE);
389         transformer.transform(new StreamSource(inputStream), invalidMessage.getPayloadResult());
390         context = new DefaultMessageContext(invalidMessage, soap11Factory);
391 
392         boolean result = interceptor.handleRequest(context, null);
393         Assert.assertTrue("Invalid response from interceptor", result);
394         Assert.assertFalse("Context has response", context.hasResponse());
395     }
396 
397 }