Add support for Spring WS auto WSDL/XSD exposure

This commit adds support for auto-configuration of Spring WS automatic
WSDL and XSD exposure i.e. registration of `WsdlDefinition` and
`XsdDefinition` beans. The bean registration is triggered by configuring
`spring.webservices.wsdl-locations` property which will search the
provided locations for WSDL/XSD files and register appropriate beans.

See gh-9635
This commit is contained in:
Vedran Pavic 2017-06-29 13:36:14 +02:00 committed by Stephane Nicoll
parent 32102c693b
commit bb72a4abe1
9 changed files with 164 additions and 7 deletions

View File

@ -16,23 +16,40 @@
package org.springframework.boot.autoconfigure.webservices;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;
import org.springframework.ws.config.annotation.EnableWs;
import org.springframework.ws.config.annotation.WsConfigurationSupport;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.wsdl.wsdl11.SimpleWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
/**
* {@link EnableAutoConfiguration Auto-configuration} for Spring Web Services.
@ -72,10 +89,76 @@ public class WebServicesAutoConfiguration {
return registration;
}
@Bean
@ConditionalOnProperty(prefix = "spring.webservices", name = "wsdl-locations")
public static WsdlDefinitionBeanFactoryPostProcessor wsdlDefinitionBeanFactoryPostProcessor() {
return new WsdlDefinitionBeanFactoryPostProcessor();
}
@Configuration
@EnableWs
protected static class WsConfiguration {
}
private static class WsdlDefinitionBeanFactoryPostProcessor
implements BeanDefinitionRegistryPostProcessor, ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
throws BeansException {
Binder binder = Binder.get(this.applicationContext.getEnvironment());
List<String> wsdlLocations = binder
.bind("spring.webservices.wsdl-locations",
Bindable.listOf(String.class))
.orElse(Collections.emptyList());
for (String wsdlLocation : wsdlLocations) {
registerBeans(wsdlLocation, "*.wsdl", SimpleWsdl11Definition.class, registry);
registerBeans(wsdlLocation, "*.xsd", SimpleXsdSchema.class, registry);
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
private void registerBeans(String location, String pattern, Class<?> type,
BeanDefinitionRegistry registry) {
Resource[] resources = new Resource[] {};
try {
resources = this.applicationContext
.getResources(ensureTrailingSlash(location) + pattern);
}
catch (IOException ignored) {
}
for (Resource resource : resources) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(type);
ConstructorArgumentValues constructorArguments = new ConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0, resource);
beanDefinition.setConstructorArgumentValues(constructorArguments);
registry.registerBeanDefinition(
StringUtils.stripFilenameExtension(resource.getFilename()),
beanDefinition);
}
}
private static String ensureTrailingSlash(String path) {
if (!path.endsWith("/")) {
return path + "/";
}
return path;
}
}
}

View File

@ -1183,6 +1183,11 @@
{
"name": "spring.thymeleaf.suffix",
"defaultValue": ".html"
},
{
"name": "spring.webservices.wsdl-locations",
"type": "java.util.List<java.lang.String>",
"description": "Comma-separated list of locations of WSDLs and accompanying XSDs to be exposed as beans."
}
],"hints": [
{

View File

@ -26,6 +26,8 @@ import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.ws.wsdl.wsdl11.SimpleWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import static org.assertj.core.api.Assertions.assertThat;
@ -90,6 +92,18 @@ public class WebServicesAutoConfigurationTests {
.containsEntry("key2", "value2"));
}
@Test
public void withWsdlBeans() {
this.contextRunner
.withPropertyValues("spring.webservices.wsdl-locations=classpath:/wsdl")
.run(context -> {
assertThat(context.getBeansOfType(SimpleWsdl11Definition.class))
.hasSize(1).containsKey("service");
assertThat(context.getBeansOfType(SimpleXsdSchema.class)).hasSize(1)
.containsKey("types");
});
}
private Collection<String> getUrlMappings(ApplicationContext context) {
return getServletRegistrationBean(context).getUrlMappings();
}

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:tns="http://www.springframework.org/spring-ws/wsdl"
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
targetNamespace="http://www.springframework.org/spring-ws/wsdl">
<wsdl:types>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
targetNamespace="http://www.springframework.org/spring-ws/wsdl">
<xsd:element name="request" type="xsd:string"/>
<xsd:element name="response" type="xsd:string"/>
</xsd:schema>
</wsdl:types>
<wsdl:message name="responseMessage">
<wsdl:part name="body" element="tns:response"/>
</wsdl:message>
<wsdl:message name="requestMessage">
<wsdl:part name="body" element="tns:request"/>
</wsdl:message>
<wsdl:portType name="portType">
<wsdl:operation name="operation">
<wsdl:input message="tns:requestMessage" name="request"/>
<wsdl:output message="tns:responseMessage" name="response"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="binding" type="tns:portType">
<wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="operation">
<wsdlsoap:operation soapAction=""/>
<wsdl:input name="request">
<wsdlsoap:body use="literal"/>
</wsdl:input>
<wsdl:output name="response">
<wsdlsoap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="service">
<wsdl:port binding="tns:binding" name="port">
<wsdlsoap:address location="/services"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
targetNamespace="http://www.springframework.org/spring-ws/wsdl/schemas">
<element name="request" type="string"/>
<element name="response" type="string"/>
</schema>

View File

@ -472,6 +472,7 @@ content into your application; rather pick only the properties that you need.
spring.webservices.path=/services # Path that serves as the base URI for the services.
spring.webservices.servlet.init= # Servlet init parameters to pass to Spring Web Services.
spring.webservices.servlet.load-on-startup=-1 # Load on startup priority of the Spring Web Services servlet.
spring.webservices.wsdl-locations= # Comma-separated list of locations of WSDLs and accompanying XSDs to be exposed as beans.
[[common-application-properties-security]]

View File

@ -6560,6 +6560,11 @@ your `Endpoints`.
The {spring-webservices-reference}[Spring Web Services features] can be easily accessed
via the `spring-boot-starter-webservices` module.
Spring Boot can also automatically expose your WSDLs and XSDs using
`spring.webservices.wsdl-locations` configuration property. For the detected WSDL and XSD
files Boot will register beans of type `SimpleWsdl11Definition` and `SimpleXsdSchema`,
respectively.
[[boot-features-developing-auto-configuration]]

View File

@ -18,10 +18,8 @@ package sample.webservices;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.ws.config.annotation.WsConfigurerAdapter;
import org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;
import org.springframework.xml.xsd.SimpleXsdSchema;
import org.springframework.xml.xsd.XsdSchema;
@Configuration
@ -37,9 +35,4 @@ public class WebServiceConfig extends WsConfigurerAdapter {
return wsdl;
}
@Bean
public XsdSchema countriesSchema() {
return new SimpleXsdSchema(new ClassPathResource("META-INF/schemas/hr.xsd"));
}
}

View File

@ -0,0 +1 @@
spring.webservices.wsdl-locations=classpath:META-INF/schemas/