From 13f537fa3116e667f65d10577a5059ccab4ff7e8 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Tue, 14 Aug 2012 10:13:33 +0000 Subject: [PATCH] SWS-791 - Transform schemaLocation in WsdlDefinitionHandlerAdapter --- .../http/WsdlDefinitionHandlerAdapter.java | 175 ++++++++++++------ .../WsdlDefinitionHandlerAdapterTest.java | 46 ++--- .../ws/transport/http/echo-expected.wsdl | 6 +- .../ws/transport/http/echo-input.wsdl | 4 +- 4 files changed, 138 insertions(+), 93 deletions(-) diff --git a/core/src/main/java/org/springframework/ws/transport/http/WsdlDefinitionHandlerAdapter.java b/core/src/main/java/org/springframework/ws/transport/http/WsdlDefinitionHandlerAdapter.java index 649b6035..a50893d0 100644 --- a/core/src/main/java/org/springframework/ws/transport/http/WsdlDefinitionHandlerAdapter.java +++ b/core/src/main/java/org/springframework/ws/transport/http/WsdlDefinitionHandlerAdapter.java @@ -1,11 +1,11 @@ /* - * Copyright 2005-2010 the original author or authors. + * Copyright 2005-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -43,27 +43,27 @@ import org.w3c.dom.Document; import org.w3c.dom.Node; /** - * Adapter to use the WsdlDefinition interface with the generic DispatcherServlet. + * Adapter to use the {@code WsdlDefinition} interface with the generic {@code DispatcherServlet}. *

- * Reads the source from the mapped WsdlDefinition implementation, and writes that as the result to the - * HttpServletResponse. + * Reads the source from the mapped {@code WsdlDefinition} implementation, and writes that as the result to the + * {@code HttpServletResponse}. *

- * If the property transformLocations is set to true, this adapter will change - * location attributes in the WSDL definition to reflect the URL of the incoming request. If the location + * If the property {@code transformLocations} is set to {@code true}, this adapter will change + * {@code location} attributes in the WSDL definition to reflect the URL of the incoming request. If the location * field in the original WSDL is an absolute path, the scheme, hostname, and port will be changed. If the location is a * relative path, the scheme, hostname, port, and context path will be prepended. This behavior can be customized by - * overriding the transformLocation() method. + * overriding the {@code transformLocation()} method. *

- * For instance, if the location attribute defined in the WSDL is http://localhost:8080/context/services/myService, - * and the request URI for the WSDL is http://example.com/context/myService.wsdl, the location will be - * changed to http://example.com/context/services/myService. + * For instance, if the location attribute defined in the WSDL is {@code http://localhost:8080/context/services/myService}, + * and the request URI for the WSDL is {@code http://example.com/context/myService.wsdl}, the location will be + * changed to {@code http://example.com/context/services/myService}. *

- * If the location attribute defined in the WSDL is /services/myService, and the request URI for the WSDL - * is http://example.com:8080/context/myService.wsdl, the location will be changed to - * http://example.com:8080/context/services/myService. + * If the location attribute defined in the WSDL is {@code /services/myService}, and the request URI for the WSDL + * is {@code http://example.com:8080/context/myService.wsdl}, the location will be changed to + * {@code http://example.com:8080/context/services/myService}. *

- * When transformLocations is enabled, all location attributes found in the WSDL definition - * are changed by default. This behavior can be customized by changing the locationExpression property, + * When {@code transformLocations} is enabled, all {@code location} attributes found in the WSDL definition + * are changed by default. This behavior can be customized by changing the {@code locationExpression} property, * which is an XPath expression that matches the attributes to change. * * @author Arjen Poutsma @@ -75,9 +75,12 @@ import org.w3c.dom.Node; */ public class WsdlDefinitionHandlerAdapter extends TransformerObjectSupport implements HandlerAdapter, InitializingBean { - /** Default XPath expression used for extracting all location attributes from the WSDL definition. */ + /** Default XPath expression used for extracting all {@code location} attributes from the WSDL definition. */ public static final String DEFAULT_LOCATION_EXPRESSION = "//@location"; + /** Default XPath expression used for extracting all {@code schemaLocation} attributes from the WSDL definition. */ + public static final String DEFAULT_SCHEMA_LOCATION_EXPRESSION = "//@schemaLocation"; + private static final String CONTENT_TYPE = "text/xml"; private static final Log logger = LogFactory.getLog(WsdlDefinitionHandlerAdapter.class); @@ -86,29 +89,50 @@ public class WsdlDefinitionHandlerAdapter extends TransformerObjectSupport imple private String locationExpression = DEFAULT_LOCATION_EXPRESSION; + private String schemaLocationExpression = DEFAULT_SCHEMA_LOCATION_EXPRESSION; + private XPathExpression locationXPathExpression; + private XPathExpression schemaLocationXPathExpression; + private boolean transformLocations = false; + private boolean transformSchemaLocations = false; + /** - * Sets the XPath expression used for extracting the location attributes from the WSDL 1.1 definition. + * Sets the XPath expression used for extracting the {@code location} attributes from the WSDL 1.1 definition. *

- * Defaults to DEFAULT_LOCATION_EXPRESSION. - * - * @see #DEFAULT_LOCATION_EXPRESSION + * Defaults to {@code DEFAULT_LOCATION_EXPRESSION}. */ public void setLocationExpression(String locationExpression) { this.locationExpression = locationExpression; } + /** + * Sets the XPath expression used for extracting the {@code schemaLocation} attributes from the WSDL 1.1 definition. + *

+ * Defaults to {@code DEFAULT_SCHEMA_LOCATION_EXPRESSION}. + */ + public void setSchemaLocationExpression(String schemaLocationExpression) { + this.schemaLocationExpression = schemaLocationExpression; + } + /** * Sets whether relative address locations in the WSDL are to be transformed using the request URI of the incoming - * HttpServletRequest. Defaults to false. + * {@code HttpServletRequest}. Defaults to {@code false}. */ public void setTransformLocations(boolean transformLocations) { this.transformLocations = transformLocations; } + /** + * Sets whether relative address schema locations in the WSDL are to be transformed using the request URI of the + * incoming {@code HttpServletRequest}. Defaults to {@code false}. + */ + public void setTransformSchemaLocations(boolean transformSchemaLocations) { + this.transformSchemaLocations = transformSchemaLocations; + } + public long getLastModified(HttpServletRequest request, Object handler) { Source definitionSource = ((WsdlDefinition) handler).getSource(); return LastModifiedHelper.getLastModified(definitionSource); @@ -117,17 +141,25 @@ public class WsdlDefinitionHandlerAdapter extends TransformerObjectSupport imple public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (HttpTransportConstants.METHOD_GET.equals(request.getMethod())) { - response.setContentType(CONTENT_TYPE); - Transformer transformer = createTransformer(); WsdlDefinition definition = (WsdlDefinition) handler; + + Transformer transformer = createTransformer(); Source definitionSource = definition.getSource(); - if (transformLocations) { + + if (transformLocations || transformSchemaLocations) { DOMResult domResult = new DOMResult(); transformer.transform(definitionSource, domResult); Document definitionDocument = (Document) domResult.getNode(); - transformLocations(definitionDocument, request); + if (transformLocations) { + transformLocations(definitionDocument, request); + } + if (transformSchemaLocations) { + transformSchemaLocations(definitionDocument, request); + } definitionSource = new DOMSource(definitionDocument); } + + response.setContentType(CONTENT_TYPE); StreamResult responseResult = new StreamResult(response.getOutputStream()); transformer.transform(definitionSource, responseResult); } @@ -144,22 +176,72 @@ public class WsdlDefinitionHandlerAdapter extends TransformerObjectSupport imple public void afterPropertiesSet() throws Exception { locationXPathExpression = XPathExpressionFactory.createXPathExpression(locationExpression, expressionNamespaces); + schemaLocationXPathExpression = + XPathExpressionFactory.createXPathExpression(schemaLocationExpression, expressionNamespaces); } + /** + * Transforms all {@code location} attributes to reflect the server name given {@code HttpServletRequest}. + * Determines the suitable attributes by evaluating the defined XPath expression, and delegates to + * {@code transformLocation} to do the transformation for all attributes that match. + *

+ * This method is only called when the {@code transformLocations} property is true. + * + * @see #setLocationExpression(String) + * @see #setTransformLocations(boolean) + * @see #transformLocation(String,javax.servlet.http.HttpServletRequest) + */ + protected void transformLocations(Document definitionDocument, HttpServletRequest request) throws Exception { + transformLocationsInternal(locationXPathExpression, definitionDocument, request); + } + + /** + * Transforms all {@code schemaLocation} attributes to reflect the server name given {@code HttpServletRequest}. + * Determines the suitable attributes by evaluating the defined XPath expression, and delegates to + * {@code transformLocation} to do the transformation for all attributes that match. + *

+ * This method is only called when the {@code transformSchemaLocations} property is true. + * + * @see #setSchemaLocationExpression(String) + * @see #setTransformSchemaLocations(boolean) + * @see #transformLocation(String,javax.servlet.http.HttpServletRequest) + */ + protected void transformSchemaLocations(Document definitionDocument, HttpServletRequest request) throws Exception { + transformLocationsInternal(schemaLocationXPathExpression, definitionDocument, request); + } + + private void transformLocationsInternal(XPathExpression xPathExpression, + Document definitionDocument, + HttpServletRequest request) throws Exception { + List locationNodes = xPathExpression.evaluateAsNodeList(definitionDocument); + for (Node locationNode : locationNodes) { + if (locationNode instanceof Attr) { + Attr location = (Attr) locationNode; + if (StringUtils.hasLength(location.getValue())) { + String newLocation = transformLocation(location.getValue(), request); + if (logger.isDebugEnabled()) { + logger.debug("Transforming [" + location.getValue() + "] to [" + newLocation + "]"); + } + location.setValue(newLocation); + } + } + } + } + /** * Transform the given location string to reflect the given request. If the given location is a full url, the * scheme, server name, and port are changed. If it is a relative url, the scheme, server name, and port are * prepended. Can be overridden in subclasses to change this behavior. *

- * For instance, if the location attribute defined in the WSDL is http://localhost:8080/context/services/myService, - * and the request URI for the WSDL is http://example.com:8080/context/myService.wsdl, the location - * will be changed to http://example.com:8080/context/services/myService. + * For instance, if the location attribute defined in the WSDL is {@code http://localhost:8080/context/services/myService}, + * and the request URI for the WSDL is {@code http://example.com:80/context/myService.wsdl}, the location + * will be changed to {@code http://example.com:80/context/services/myService}. *

- * If the location attribute defined in the WSDL is /services/myService, and the request URI for the - * WSDL is http://example.com:8080/context/myService.wsdl, the location will be changed to - * http://example.com:8080/context/services/myService. + * If the location attribute defined in the WSDL is {@code /services/myService}, and the request URI for the + * WSDL is {@code http://example.com:8080/context/myService.wsdl}, the location will be changed to + * {@code http://example.com:8080/context/services/myService}. *

- * This method is only called when the transformLocations property is true. + * This method is only called when the {@code transformLocations} property is true. */ protected String transformLocation(String location, HttpServletRequest request) { StringBuilder url = new StringBuilder(request.getScheme()); @@ -184,31 +266,4 @@ public class WsdlDefinitionHandlerAdapter extends TransformerObjectSupport imple // unknown location, return the original return location; } - - /** - * Transforms all location attributes to reflect the server name given HttpServletRequest. - * Determines the suitable attributes by evaluating the defined XPath expression, and delegates to - * transformLocation to do the transformation for all attributes that match. - *

- * This method is only called when the transformLocations property is true. - * - * @see #setLocationExpression(String) - * @see #setTransformLocations(boolean) - * @see #transformLocation(String,javax.servlet.http.HttpServletRequest) - */ - protected void transformLocations(Document definitionDocument, HttpServletRequest request) throws Exception { - List locationNodes = locationXPathExpression.evaluateAsNodeList(definitionDocument); - for (Node locationNode : locationNodes) { - if (locationNode instanceof Attr) { - Attr location = (Attr) locationNode; - if (StringUtils.hasLength(location.getValue())) { - String newLocation = transformLocation(location.getValue(), request); - if (logger.isDebugEnabled()) { - logger.debug("Transforming [" + location.getValue() + "] to [" + newLocation + "]"); - } - location.setValue(newLocation); - } - } - } - } } diff --git a/core/src/test/java/org/springframework/ws/transport/http/WsdlDefinitionHandlerAdapterTest.java b/core/src/test/java/org/springframework/ws/transport/http/WsdlDefinitionHandlerAdapterTest.java index 035e45ac..93fbf192 100644 --- a/core/src/test/java/org/springframework/ws/transport/http/WsdlDefinitionHandlerAdapterTest.java +++ b/core/src/test/java/org/springframework/ws/transport/http/WsdlDefinitionHandlerAdapterTest.java @@ -1,11 +1,11 @@ /* - * Copyright 2005-2010 the original author or authors. + * Copyright 2005-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -20,9 +20,6 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; import java.net.URI; import javax.servlet.http.HttpServletResponse; -import javax.wsdl.Definition; -import javax.wsdl.factory.WSDLFactory; -import javax.wsdl.xml.WSDLReader; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -37,7 +34,6 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.w3c.dom.Document; -import org.xml.sax.InputSource; import static org.custommonkey.xmlunit.XMLAssert.assertXMLEqual; import static org.easymock.EasyMock.*; @@ -62,7 +58,7 @@ public class WsdlDefinitionHandlerAdapterTest { } @Test - public void testHandleGet() throws Exception { + public void handleGet() throws Exception { request.setMethod(HttpTransportConstants.METHOD_GET); String definition = ""; expect(definitionMock.getSource()).andReturn(new StringSource(definition)); @@ -76,7 +72,7 @@ public class WsdlDefinitionHandlerAdapterTest { } @Test - public void testHandleNonGet() throws Exception { + public void handleNonGet() throws Exception { request.setMethod(HttpTransportConstants.METHOD_POST); replay(definitionMock); @@ -89,7 +85,7 @@ public class WsdlDefinitionHandlerAdapterTest { } @Test - public void testTransformLocations() throws Exception { + public void transformLocations() throws Exception { adapter.setTransformLocations(true); request.setMethod(HttpTransportConstants.METHOD_GET); request.setScheme("http"); @@ -114,7 +110,7 @@ public class WsdlDefinitionHandlerAdapterTest { } @Test - public void testTransformLocationFullUrl() throws Exception { + public void transformLocationFullUrl() throws Exception { request.setScheme("http"); request.setServerName("example.com"); request.setServerPort(8080); @@ -129,7 +125,7 @@ public class WsdlDefinitionHandlerAdapterTest { } @Test - public void testTransformLocationEmptyContextFullUrl() throws Exception { + public void transformLocationEmptyContextFullUrl() throws Exception { request.setScheme("http"); request.setServerName("example.com"); request.setServerPort(8080); @@ -143,7 +139,7 @@ public class WsdlDefinitionHandlerAdapterTest { } @Test - public void testTransformLocationRelativeUrl() throws Exception { + public void transformLocationRelativeUrl() throws Exception { request.setScheme("http"); request.setServerName("example.com"); request.setServerPort(8080); @@ -158,7 +154,7 @@ public class WsdlDefinitionHandlerAdapterTest { } @Test - public void testTransformLocationEmptyContextRelativeUrl() throws Exception { + public void transformLocationEmptyContextRelativeUrl() throws Exception { request.setScheme("http"); request.setServerName("example.com"); request.setServerPort(8080); @@ -172,7 +168,7 @@ public class WsdlDefinitionHandlerAdapterTest { } @Test - public void testHandleSimpleWsdl11DefinitionWithoutTransformLocations() throws Exception { + public void handleSimpleWsdl11DefinitionWithoutTransformLocations() throws Exception { adapter.setTransformLocations(false); request.setMethod(HttpTransportConstants.METHOD_GET); request.setScheme("http"); @@ -187,14 +183,8 @@ public class WsdlDefinitionHandlerAdapterTest { new SimpleWsdl11Definition(new ClassPathResource("echo-input.wsdl", getClass())); adapter.handle(request, response, definition); + InputStream inputStream = new ByteArrayInputStream(response.getContentAsByteArray()); - - WSDLFactory factory = WSDLFactory.newInstance(); - WSDLReader reader = factory.newWSDLReader(); - Definition wsdl4jDefinition = reader.readWSDL(null, new InputSource(inputStream)); - Assert.assertNotNull("No definition read", wsdl4jDefinition); - - inputStream = new ByteArrayInputStream(response.getContentAsByteArray()); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setNamespaceAware(true); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); @@ -206,12 +196,14 @@ public class WsdlDefinitionHandlerAdapterTest { } @Test - public void testHandleSimpleWsdl11DefinitionWithTransformLocation() throws Exception { + public void handleSimpleWsdl11DefinitionWithTransformLocation() throws Exception { adapter.setTransformLocations(true); + adapter.setTransformSchemaLocations(true); + request.setMethod(HttpTransportConstants.METHOD_GET); request.setScheme("http"); request.setServerName("example.com"); - request.setServerPort(8080); + request.setServerPort(80); request.setContextPath("/context"); request.setServletPath("/service.wsdl"); request.setPathInfo(null); @@ -221,14 +213,8 @@ public class WsdlDefinitionHandlerAdapterTest { new SimpleWsdl11Definition(new ClassPathResource("echo-input.wsdl", getClass())); adapter.handle(request, response, definition); + InputStream inputStream = new ByteArrayInputStream(response.getContentAsByteArray()); - - WSDLFactory factory = WSDLFactory.newInstance(); - WSDLReader reader = factory.newWSDLReader(); - Definition wsdl4jDefinition = reader.readWSDL(null, new InputSource(inputStream)); - Assert.assertNotNull("No definition read", wsdl4jDefinition); - - inputStream = new ByteArrayInputStream(response.getContentAsByteArray()); DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setNamespaceAware(true); DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); diff --git a/core/src/test/resources/org/springframework/ws/transport/http/echo-expected.wsdl b/core/src/test/resources/org/springframework/ws/transport/http/echo-expected.wsdl index e3482237..4901deae 100644 --- a/core/src/test/resources/org/springframework/ws/transport/http/echo-expected.wsdl +++ b/core/src/test/resources/org/springframework/ws/transport/http/echo-expected.wsdl @@ -5,8 +5,10 @@ targetNamespace="http://www.springframework.org/spring-ws/samples/echo"> + @@ -43,7 +45,7 @@ - + diff --git a/core/src/test/resources/org/springframework/ws/transport/http/echo-input.wsdl b/core/src/test/resources/org/springframework/ws/transport/http/echo-input.wsdl index 62ce2aae..52ec55a1 100644 --- a/core/src/test/resources/org/springframework/ws/transport/http/echo-input.wsdl +++ b/core/src/test/resources/org/springframework/ws/transport/http/echo-input.wsdl @@ -5,8 +5,10 @@ targetNamespace="http://www.springframework.org/spring-ws/samples/echo"> +