Introduced @Configuration support for Spring-WS

Introduced support for Java @Configuration classes in the form of the
@EnableWS annotation, WsConfigurationSupport and WsConfigurer.
Overall solution is quite similar to Spring-MVC's @EnableMvc.

Also added/uopdated reference documentation for the various
@Configuration options.

Issue: SWS-836
This commit is contained in:
Arjen Poutsma
2014-01-30 14:24:01 +01:00
parent 3522effaea
commit 160907a6e1
21 changed files with 1314 additions and 94 deletions

View File

@@ -0,0 +1,47 @@
package org.springframework.ws.config.annotation;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.ws.server.EndpointInterceptor;
import org.springframework.ws.server.endpoint.adapter.method.MethodArgumentResolver;
import org.springframework.ws.server.endpoint.adapter.method.MethodReturnValueHandler;
/**
* A sub-class of {@code WsConfigurationSupport} that detects and delegates
* to all beans of type {@link WsConfigurer} allowing them to customize the
* configuration provided by {@code WsConfigurationSupport}. This is the
* class actually imported by {@link EnableWs @EnableWs}.
*
* @author Arjen Poutsma
* @since 2.2
*/
@Configuration
public class DelegatingWsConfiguration extends WsConfigurationSupport {
private final WsConfigurerComposite configurers = new WsConfigurerComposite();
@Autowired(required = false)
public void setConfigurers(List<WsConfigurer> configurers) {
if (configurers != null && !configurers.isEmpty()) {
this.configurers.addWsConfigurers(configurers);
}
}
@Override
protected void addInterceptors(List<EndpointInterceptor> interceptors) {
this.configurers.addInterceptors(interceptors);
}
@Override
protected void addArgumentResolvers(List<MethodArgumentResolver> argumentResolvers) {
this.configurers.addArgumentResolvers(argumentResolvers);
}
@Override
protected void addReturnValueHandlers(
List<MethodReturnValueHandler> returnValueHandlers) {
this.configurers.addReturnValueHandlers(returnValueHandlers);
}
}

View File

@@ -0,0 +1,102 @@
/*
* Copyright 2005-2014 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.config.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
/**
* Add this annotation to an {@link Configuration @Configuration} class to have the Spring
* Web Services configuration defined in {@link WsConfigurationSupport} imported. For
* instance:
*
* <pre class="code">
* &#064;Configuration
* &#064;EnableWs
* &#064;ComponentScan(basePackageClasses = { MyConfiguration.class })
* public class MyWsConfiguration {
*
* }
* </pre>
* <p>Customize the imported configuration by implementing the
* {@link WsConfigurer} interface or more likely by extending the
* {@link WsConfigurerAdapter} base class and overriding individual methods:
*
* <pre class="code">
* &#064;Configuration
* &#064;EnableWs
* &#064;ComponentScan(basePackageClasses = { MyConfiguration.class })
* public class MyConfiguration extends WsConfigurerAdapter {
*
* &#064;Override
* public void addInterceptors(List&lt;EndpointInterceptor&gt; interceptors) {
* interceptors.add(new MyInterceptor());
* }
*
* &#064;Override
* public void addArgumentResolvers(List&lt;MethodArgumentResolver&gt; argumentResolvers) {
* argumentResolvers.add(new MyArgumentResolver());
* }
*
* // More overridden methods ...
* }
* </pre>
*
* <p>If the customization options of {@link WsConfigurer} do not expose
* something you need to configure, consider removing the {@code @EnableWs}
* annotation and extending directly from {@link WsConfigurationSupport}
* overriding selected {@code @Bean} methods:
*
* <pre class="code">
* &#064;Configuration
* &#064;ComponentScan(basePackageClasses = { MyConfiguration.class })
* public class MyConfiguration extends WsConfigurationSupport {
*
* &#064;Override
* public void addInterceptors(List&lt;EndpointInterceptor&gt; interceptors) {
* interceptors.add(new MyInterceptor());
* }
*
* &#064;Bean
* &#064;Override
* public DefaultMethodEndpointAdapter defaultMethodEndpointAdapter() {
* // Create or delegate to "super" to create and
* // customize properties of DefaultMethodEndpointAdapter
* }
* }
* </pre>
*
* @see WsConfigurer
* @see WsConfigurerAdapter
* @see WsConfigurationSupport
*
* @author Arjen Poutsma
* @since 2.2
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWsConfiguration.class)
public @interface EnableWs {
}

View File

@@ -0,0 +1,217 @@
/*
* Copyright 2005-2014 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.config.annotation;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.ws.server.EndpointAdapter;
import org.springframework.ws.server.EndpointExceptionResolver;
import org.springframework.ws.server.EndpointInterceptor;
import org.springframework.ws.server.EndpointMapping;
import org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter;
import org.springframework.ws.server.endpoint.adapter.method.MethodArgumentResolver;
import org.springframework.ws.server.endpoint.adapter.method.MethodReturnValueHandler;
import org.springframework.ws.server.endpoint.annotation.PayloadRoot;
import org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping;
import org.springframework.ws.soap.addressing.server.AnnotationActionEndpointMapping;
import org.springframework.ws.soap.addressing.server.annotation.Action;
import org.springframework.ws.soap.server.endpoint.SimpleSoapExceptionResolver;
import org.springframework.ws.soap.server.endpoint.SoapFaultAnnotationExceptionResolver;
import org.springframework.ws.soap.server.endpoint.annotation.SoapAction;
import org.springframework.ws.soap.server.endpoint.annotation.SoapFault;
import org.springframework.ws.soap.server.endpoint.mapping.SoapActionAnnotationMethodEndpointMapping;
/**
* This is the main class providing the configuration behind the Spring Web Services Java
* config. It is typically imported by adding {@link EnableWs @EnableWs} to an
* application {@link Configuration @Configuration} class. An alternative, more
* advanced option is to extend directly from this class and override methods as
* necessary remembering to add {@link Configuration @Configuration} to the
* subclass and {@link Bean @Bean} to overridden {@link Bean @Bean} methods.
* For more details see the Javadoc of {@link EnableWs @EnableWs}.
*
* <p>This class registers the following {@link EndpointMapping}s:
* <ul>
* <li>{@link PayloadRootAnnotationMethodEndpointMapping}
* ordered at 0 for mapping requests to {@link PayloadRoot @PayloadRoot} annotated
* controller methods.
* <li>{@link SoapActionAnnotationMethodEndpointMapping}
* ordered at 1 for mapping requests to {@link SoapAction @SoapAction} annotated
* controller methods.
* <li>{@link AnnotationActionEndpointMapping}
* ordered at 2 for mapping requests to {@link Action @Action} annotated
* controller methods.
* </ul>
*
* <p>Registers one {@link EndpointAdapter}:
* <ul>
* <li>{@link DefaultMethodEndpointAdapter}
* for processing requests with annotated endpoint methods.
* </ul>
*
* <p>Registers the following {@link EndpointExceptionResolver}s:
* <ul>
* <li>{@link SoapFaultAnnotationExceptionResolver} for handling exceptions
* annotated with {@link SoapFault @SoapFault}.
* <li>{@link SimpleSoapExceptionResolver} for creating default exceptions.
* </ul>
*
* @see EnableWs
* @see WsConfigurer
* @see WsConfigurerAdapter
*
* @author Arjen Poutsma
* @since 2.2
*/
public class WsConfigurationSupport {
private List<EndpointInterceptor> interceptors;
/**
* Returns a {@link PayloadRootAnnotationMethodEndpointMapping} ordered at 0 for
* mapping requests to annotated endpoints.
*/
@Bean
public PayloadRootAnnotationMethodEndpointMapping payloadRootAnnotationMethodEndpointMapping() {
PayloadRootAnnotationMethodEndpointMapping endpointMapping =
new PayloadRootAnnotationMethodEndpointMapping();
endpointMapping.setOrder(0);
endpointMapping.setInterceptors(getInterceptors());
return endpointMapping;
}
/**
* Returns a {@link SoapActionAnnotationMethodEndpointMapping} ordered at 1 for
* mapping requests to annotated endpoints.
*/
@Bean
public SoapActionAnnotationMethodEndpointMapping soapActionAnnotationMethodEndpointMapping() {
SoapActionAnnotationMethodEndpointMapping endpointMapping =
new SoapActionAnnotationMethodEndpointMapping();
endpointMapping.setOrder(1);
endpointMapping.setInterceptors(getInterceptors());
return endpointMapping;
}
/**
* Returns a {@link AnnotationActionEndpointMapping} ordered at 2 for
* mapping requests to annotated endpoints.
*/
@Bean
public AnnotationActionEndpointMapping annotationActionEndpointMapping() {
AnnotationActionEndpointMapping endpointMapping =
new AnnotationActionEndpointMapping();
endpointMapping.setOrder(2);
endpointMapping.setPostInterceptors(getInterceptors());
return endpointMapping;
}
/**
* Provide access to the shared handler interceptors used to configure
* {@link EndpointMapping} instances with. This method cannot be overridden,
* use {@link #addInterceptors(List)} instead.
*/
protected final EndpointInterceptor[] getInterceptors() {
if (interceptors == null) {
interceptors = new ArrayList<EndpointInterceptor>();
addInterceptors(interceptors);
}
return interceptors.toArray(new EndpointInterceptor[interceptors.size()]);
}
/**
* Template method to add endpoint interceptors. Override this method to add Spring-WS
* interceptors for pre- and post-processing of endpoint invocation.
*/
protected void addInterceptors(List<EndpointInterceptor> interceptors) {
}
/**
* Returns a {@link DefaultMethodEndpointAdapter} for processing requests
* through annotated endpoint methods. Consider overriding one of these
* other more fine-grained methods:
* <ul>
* <li>{@link #addArgumentResolvers(List)} for adding custom argument resolvers.
* <li>{@link #addReturnValueHandlers(List)} for adding custom return value handlers.
* </ul>
*/
@Bean
public DefaultMethodEndpointAdapter defaultMethodEndpointAdapter() {
List<MethodArgumentResolver> argumentResolvers =
new ArrayList<MethodArgumentResolver>();
addArgumentResolvers(argumentResolvers);
List<MethodReturnValueHandler> returnValueHandlers =
new ArrayList<MethodReturnValueHandler>();
addReturnValueHandlers(returnValueHandlers);
DefaultMethodEndpointAdapter adapter = new DefaultMethodEndpointAdapter();
adapter.setCustomMethodArgumentResolvers(argumentResolvers);
adapter.setCustomMethodReturnValueHandlers(returnValueHandlers);
return adapter;
}
/**
* Add custom {@link MethodArgumentResolver}s to use in addition to
* the ones registered by default.
* @param argumentResolvers the list of custom converters;
* initially an empty list.
*/
protected void addArgumentResolvers(List<MethodArgumentResolver> argumentResolvers) {
}
/**
* Add custom {@link MethodReturnValueHandler}s in addition to the
* ones registered by default.
* @param returnValueHandlers the list of custom handlers;
* initially an empty list.
*/
protected void addReturnValueHandlers(
List<MethodReturnValueHandler> returnValueHandlers) {
}
/**
* Returns a {@link SoapFaultAnnotationExceptionResolver} ordered at 0 for handling
* endpoint exceptions.
*/
@Bean
public SoapFaultAnnotationExceptionResolver soapFaultAnnotationExceptionResolver() {
SoapFaultAnnotationExceptionResolver exceptionResolver = new SoapFaultAnnotationExceptionResolver();
exceptionResolver.setOrder(0);
return exceptionResolver;
}
/**
* Returns a {@link SimpleSoapExceptionResolver} ordered at
* {@linkplain Ordered#LOWEST_PRECEDENCE lowest precedence} for handling endpoint
* exceptions.
*/
@Bean
public SimpleSoapExceptionResolver simpleSoapExceptionResolver() {
SimpleSoapExceptionResolver exceptionResolver = new SimpleSoapExceptionResolver();
exceptionResolver.setOrder(Ordered.LOWEST_PRECEDENCE);
return exceptionResolver;
}
}

View File

@@ -0,0 +1,43 @@
package org.springframework.ws.config.annotation;
import java.util.List;
import org.springframework.ws.server.EndpointInterceptor;
import org.springframework.ws.server.endpoint.adapter.method.MethodArgumentResolver;
import org.springframework.ws.server.endpoint.adapter.method.MethodReturnValueHandler;
/**
* Defines callback methods to customize the Java-based configuration for
* Spring Web Services enabled via {@link EnableWs @EnableWs}.
*
* <p>{@code @EnableWs}-annotated configuration classes may implement
* this interface to be called back and given a chance to customize the
* default configuration. Consider extending {@link WsConfigurerAdapter},
* which provides a stub implementation of all interface methods.
*
* @author Arjen Poutsma
* @since 2.2
*/
public interface WsConfigurer {
/**
* Add {@link EndpointInterceptor}s for pre- and post-processing of
* endpoint method invocations.
*/
void addInterceptors(List<EndpointInterceptor> interceptors);
/**
* Add resolvers to support custom endpoint method argument types.
* @param argumentResolvers initially an empty list
*/
void addArgumentResolvers(List<MethodArgumentResolver> argumentResolvers);
/**
* Add handlers to support custom controller method return value types.
* <p>Using this option does not override the built-in support for handling
* return values. To customize the built-in support for handling return
* values, configure RequestMappingHandlerAdapter directly.
* @param returnValueHandlers initially an empty list
*/
void addReturnValueHandlers(List<MethodReturnValueHandler> returnValueHandlers);
}

View File

@@ -0,0 +1,43 @@
package org.springframework.ws.config.annotation;
import java.util.List;
import org.springframework.ws.server.EndpointInterceptor;
import org.springframework.ws.server.endpoint.adapter.method.MethodArgumentResolver;
import org.springframework.ws.server.endpoint.adapter.method.MethodReturnValueHandler;
/**
* An default implementation of {@link WsConfigurer} with empty methods allowing
* sub-classes to override only the methods they're interested in.
*
* @author Arjen Poutsma
* @since 2.2
*/
public class WsConfigurerAdapter implements WsConfigurer {
/**
* {@inheritDoc}
* <p>This implementation is empty.
*/
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
}
/**
* {@inheritDoc}
* <p>This implementation is empty.
*/
@Override
public void addArgumentResolvers(List<MethodArgumentResolver> argumentResolvers) {
}
/**
* {@inheritDoc}
* <p>This implementation is empty.
*/
@Override
public void addReturnValueHandlers(
List<MethodReturnValueHandler> returnValueHandlers) {
}
}

View File

@@ -0,0 +1,48 @@
package org.springframework.ws.config.annotation;
import java.util.ArrayList;
import java.util.List;
import org.springframework.ws.server.EndpointInterceptor;
import org.springframework.ws.server.endpoint.adapter.method.MethodArgumentResolver;
import org.springframework.ws.server.endpoint.adapter.method.MethodReturnValueHandler;
/**
* An {@link WsConfigurer} implementation that delegates to other {@link WsConfigurer} instances.
*
* @author Arjen Poutsma
* @since 2.2
*/
public class WsConfigurerComposite implements WsConfigurer {
private List<WsConfigurer> delegates = new ArrayList<WsConfigurer>();
public void addWsConfigurers(List<WsConfigurer> configurers) {
if (configurers != null) {
this.delegates.addAll(configurers);
}
}
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
for (WsConfigurer delegate : delegates) {
delegate.addInterceptors(interceptors);
}
}
@Override
public void addArgumentResolvers(List<MethodArgumentResolver> argumentResolvers) {
for (WsConfigurer delegate : delegates) {
delegate.addArgumentResolvers(argumentResolvers);
}
}
@Override
public void addReturnValueHandlers(
List<MethodReturnValueHandler> returnValueHandlers) {
for (WsConfigurer delegate : delegates) {
delegate.addReturnValueHandlers(returnValueHandlers);
}
}
}

View File

@@ -69,31 +69,77 @@ public class DefaultMethodEndpointAdapter extends AbstractMethodEndpointAdapter
private List<MethodArgumentResolver> methodArgumentResolvers;
private List<MethodArgumentResolver> customMethodArgumentResolvers;
private List<MethodReturnValueHandler> methodReturnValueHandlers;
private List<MethodReturnValueHandler> customMethodReturnValueHandlers;
private ClassLoader classLoader;
/** Returns the list of {@code MethodArgumentResolver}s to use. */
public List<MethodArgumentResolver> getMethodArgumentResolvers() {
/**
* Returns the list of {@code MethodArgumentResolver}s to use.
*/
public List<MethodArgumentResolver> getMethodArgumentResolvers() {
return methodArgumentResolvers;
}
/** Sets the list of {@code MethodArgumentResolver}s to use. */
public void setMethodArgumentResolvers(List<MethodArgumentResolver> methodArgumentResolvers) {
/**
* Sets the list of {@code MethodArgumentResolver}s to use.
*/
public void setMethodArgumentResolvers(List<MethodArgumentResolver> methodArgumentResolvers) {
this.methodArgumentResolvers = methodArgumentResolvers;
}
/** Returns the list of {@code MethodReturnValueHandler}s to use. */
public List<MethodReturnValueHandler> getMethodReturnValueHandlers() {
/**
* Returns the custom argument resolvers.
*/
public List<MethodArgumentResolver> getCustomMethodArgumentResolvers() {
return customMethodArgumentResolvers;
}
/**
* Sets the custom handlers for method arguments. Custom handlers are
* ordered after built-in ones. To override the built-in support for
* return value handling use {@link #setMethodArgumentResolvers(List)}.
*/
public void setCustomMethodArgumentResolvers(
List<MethodArgumentResolver> customMethodArgumentResolvers) {
this.customMethodArgumentResolvers = customMethodArgumentResolvers;
}
/**
* Returns the list of {@code MethodReturnValueHandler}s to use.
*/
public List<MethodReturnValueHandler> getMethodReturnValueHandlers() {
return methodReturnValueHandlers;
}
/** Sets the list of {@code MethodReturnValueHandler}s to use. */
public void setMethodReturnValueHandlers(List<MethodReturnValueHandler> methodReturnValueHandlers) {
/**
* Sets the list of {@code MethodReturnValueHandler}s to use.
*/
public void setMethodReturnValueHandlers(List<MethodReturnValueHandler> methodReturnValueHandlers) {
this.methodReturnValueHandlers = methodReturnValueHandlers;
}
private ClassLoader getClassLoader() {
/**
* Returns the custom return value handlers.
*/
public List<MethodReturnValueHandler> getCustomMethodReturnValueHandlers() {
return customMethodReturnValueHandlers;
}
/**
* Sets the handlers for custom return value types. Custom handlers are
* ordered after built-in ones. To override the built-in support for
* return value handling use {@link #setMethodReturnValueHandlers(List)}.
*/
public void setCustomMethodReturnValueHandlers(
List<MethodReturnValueHandler> customMethodReturnValueHandlers) {
this.customMethodReturnValueHandlers = customMethodReturnValueHandlers;
}
private ClassLoader getClassLoader() {
return this.classLoader != null ? this.classLoader : DefaultMethodEndpointAdapter.class.getClassLoader();
}
@@ -139,6 +185,9 @@ public class DefaultMethodEndpointAdapter extends AbstractMethodEndpointAdapter
if (logger.isDebugEnabled()) {
logger.debug("No MethodArgumentResolvers set, using defaults: " + methodArgumentResolvers);
}
if (getCustomMethodArgumentResolvers() != null) {
methodArgumentResolvers.addAll(getCustomMethodArgumentResolvers());
}
setMethodArgumentResolvers(methodArgumentResolvers);
}
}
@@ -180,6 +229,9 @@ public class DefaultMethodEndpointAdapter extends AbstractMethodEndpointAdapter
if (logger.isDebugEnabled()) {
logger.debug("No MethodReturnValueHandlers set, using defaults: " + methodReturnValueHandlers);
}
if (getCustomMethodReturnValueHandlers() != null) {
methodReturnValueHandlers.addAll(getCustomMethodReturnValueHandlers());
}
setMethodReturnValueHandlers(methodReturnValueHandlers);
}
}

View File

@@ -20,6 +20,9 @@ import java.io.IOException;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
@@ -37,9 +40,6 @@ import org.springframework.xml.validation.XmlValidatorFactory;
import org.springframework.xml.xsd.XsdSchema;
import org.springframework.xml.xsd.XsdSchemaCollection;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/**
* Abstract base class for <code>EndpointInterceptor</code> implementations that validate part of the message using a
* schema. The exact message part is determined by the <code>getValidationRequestSource</code> and
@@ -118,7 +118,7 @@ public abstract class AbstractValidatingInterceptor extends TransformerObjectSup
* @param schema the xsd schema to use
* @throws IOException in case of I/O errors
*/
public void setXsdSchema(XsdSchema schema) throws IOException {
public void setXsdSchema(XsdSchema schema) {
this.validator = schema.createValidator();
}
@@ -130,7 +130,7 @@ public abstract class AbstractValidatingInterceptor extends TransformerObjectSup
* @param schemaCollection the xsd schema collection to use
* @throws IOException in case of I/O errors
*/
public void setXsdSchemaCollection(XsdSchemaCollection schemaCollection) throws IOException {
public void setXsdSchemaCollection(XsdSchemaCollection schemaCollection) {
this.validator = schemaCollection.createValidator();
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2002-2014 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.transport.http.support;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
/**
* Base class for {@link WebApplicationInitializer} implementations that register a
* {@link MessageDispatcherServlet} configured with annotated classes, e.g. Spring's
* {@link Configuration @Configuration} classes.
*
* <p>Concrete implementations are required to implement {@link #getRootConfigClasses()}
* and {@link #getServletConfigClasses()} as well as {@link #getServletMappings()}.
* Further template and customization methods are provided by
* {@link AbstractDispatcherServletInitializer}.
*
* @author Arjen Poutsma
* @since 2.2
*/
public abstract class AbstractAnnotationConfigMessageDispatcherServletInitializer
extends AbstractMessageDispatcherServletInitializer {
/**
* {@inheritDoc}
* <p>This implementation creates an {@link AnnotationConfigWebApplicationContext},
* providing it the annotated classes returned by {@link #getRootConfigClasses()}.
* Returns {@code null} if {@link #getRootConfigClasses()} returns {@code null}.
*/
@Override
protected WebApplicationContext createRootApplicationContext() {
Class<?>[] configClasses = getRootConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
AnnotationConfigWebApplicationContext rootAppContext = new AnnotationConfigWebApplicationContext();
rootAppContext.register(configClasses);
return rootAppContext;
}
else {
return null;
}
}
/**
* {@inheritDoc}
* <p>This implementation creates an {@link AnnotationConfigWebApplicationContext},
* providing it the annotated classes returned by {@link #getServletConfigClasses()}.
*/
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext servletAppContext = new AnnotationConfigWebApplicationContext();
Class<?>[] configClasses = getServletConfigClasses();
if (!ObjectUtils.isEmpty(configClasses)) {
servletAppContext.register(configClasses);
}
return servletAppContext;
}
/**
* Specify {@link org.springframework.context.annotation.Configuration @Configuration}
* and/or {@link org.springframework.stereotype.Component @Component} classes to be
* provided to the {@linkplain #createRootApplicationContext() root application context}.
* @return the configuration classes for the root application context, or {@code null}
* if creation and registration of a root context is not desired
*/
protected abstract Class<?>[] getRootConfigClasses();
/**
* Specify {@link org.springframework.context.annotation.Configuration @Configuration}
* and/or {@link org.springframework.stereotype.Component @Component} classes to be
* provided to the {@linkplain #createServletApplicationContext() dispatcher servlet
* application context}.
* @return the configuration classes for the dispatcher servlet application context
* (may not be empty or {@code null})
*/
protected abstract Class<?>[] getServletConfigClasses();
}

View File

@@ -0,0 +1,159 @@
/*
* Copyright 2005-2014 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ws.transport.http.support;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.springframework.util.Assert;
import org.springframework.web.context.AbstractContextLoaderInitializer;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
/**
* Base class for {@link org.springframework.web.WebApplicationInitializer
* WebApplicationInitializer} implementations that register a
* {@link MessageDispatcherServlet} in the servlet context.
*
* <p>Concrete implementations are required to implement {@link
* #createServletApplicationContext()}, which gets invoked from
* {@link #registerMessageDispatcherServlet(ServletContext)}. Further customization can be
* achieved by overriding {@link #customizeRegistration(ServletRegistration.Dynamic)}.
*
* <p>Because this class extends from {@link AbstractContextLoaderInitializer}, concrete
* implementations are also required to implement {@link #createRootApplicationContext()}
* to set up a parent "<strong>root</strong>" application context. If a root context is
* not desired, implementations can simply return {@code null} in the
* {@code createRootApplicationContext()} implementation.
*
* @author Arjen Poutsma
* @since 2.2
*/
public abstract class AbstractMessageDispatcherServletInitializer extends
AbstractContextLoaderInitializer {
/**
* The default servlet name. Can be customized by overriding {@link #getServletName}.
*/
public static final String DEFAULT_SERVLET_NAME = "messageDispatcher";
/**
* The default servlet mappings. Can be customized by overriding {@link #getServletMappings()}.
*/
public static final String[] DEFAULT_SERVLET_MAPPINGS = new String[] { "/services", "*.wsdl"};
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
this.registerMessageDispatcherServlet(servletContext);
}
/**
* Register a {@link MessageDispatcherServlet} against the given servlet context.
* <p>This method will create a {@code MessageDispatcherServlet} with the name
* returned by {@link #getServletName()}, initializing it with the application context
* returned from {@link #createServletApplicationContext()}, and mapping it to the
* patterns returned from {@link #getServletMappings()}.
* <p>Further customization can be achieved by overriding {@link
* #customizeRegistration(ServletRegistration.Dynamic)}.
* @param servletContext the context to register the servlet against
*/
protected void registerMessageDispatcherServlet(ServletContext servletContext) {
String servletName = this.getServletName();
Assert.hasLength(servletName, "getServletName() may not return empty or null");
WebApplicationContext servletAppContext = this.createServletApplicationContext();
Assert.notNull(servletAppContext,
"createServletApplicationContext() did not return an application " +
"context for servlet [" + servletName + "]");
MessageDispatcherServlet dispatcherServlet =
new MessageDispatcherServlet(servletAppContext);
dispatcherServlet.setTransformWsdlLocations(isTransformWsdlLocations());
dispatcherServlet.setTransformSchemaLocations(isTransformSchemaLocations());
ServletRegistration.Dynamic registration =
servletContext.addServlet(servletName, dispatcherServlet);
Assert.notNull(registration,
"Failed to register servlet with name '" + servletName + "'." +
"Check if there is another servlet registered under the same name.");
registration.setLoadOnStartup(1);
registration.addMapping(getServletMappings());
this.customizeRegistration(registration);
}
/**
* Return the name under which the {@link MessageDispatcherServlet} will be registered.
* Defaults to {@link #DEFAULT_SERVLET_NAME}.
* @see #registerMessageDispatcherServlet(ServletContext)
*/
protected String getServletName() {
return DEFAULT_SERVLET_NAME;
}
/**
* Create a servlet application context to be provided to the {@code MessageDispatcherServlet}.
* <p>The returned context is delegated to Spring's
* {@link MessageDispatcherServlet#MessageDispatcherServlet(WebApplicationContext)}.
* As such, it typically contains endpoints, interceptors and other
* web service-related beans.
* @see #registerMessageDispatcherServlet(ServletContext)
*/
protected abstract WebApplicationContext createServletApplicationContext();
/**
* Specify the servlet mapping(s) for the {@code MessageDispatcherServlet}.
* Defaults to {@link #DEFAULT_SERVLET_MAPPING}.
* @see #registerMessageDispatcherServlet(ServletContext)
*/
protected String[] getServletMappings() {
return DEFAULT_SERVLET_MAPPINGS;
}
/**
* Indicates whether relative address locations in the WSDL are to be transformed
* using the request URI of the incoming HTTP request. Defaults to {@code false}.
*/
public boolean isTransformWsdlLocations() {
return false;
}
/**
* Indicates whether relative address locations in the XSD are to be transformed using
* the request URI of the incoming HTTP request. Defaults to {@code false}.
*/
protected boolean isTransformSchemaLocations() {
return false;
}
/**
* Optionally perform further registration customization once
* {@link #registerMessageDispatcherServlet(ServletContext)} has completed.
* @param registration the {@code MessageDispatcherServlet} registration to be customized
* @see #registerMessageDispatcherServlet(ServletContext)
*/
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
}
}

View File

@@ -16,7 +16,6 @@
package org.springframework.ws.wsdl.wsdl11.provider;
import java.io.IOException;
import javax.wsdl.Definition;
import javax.wsdl.Types;
import javax.wsdl.WSDLException;
@@ -64,7 +63,7 @@ public class InliningXsdSchemaTypesProvider extends TransformerObjectSupport imp
return new XsdSchema[]{schema};
}
public XmlValidator createValidator() throws IOException {
public XmlValidator createValidator() {
throw new UnsupportedOperationException();
}
};

View File

@@ -0,0 +1,86 @@
package org.springframework.ws.config.annotation;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter;
import org.springframework.ws.server.endpoint.annotation.Endpoint;
import org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping;
import org.springframework.ws.soap.addressing.server.AnnotationActionEndpointMapping;
import org.springframework.ws.soap.server.endpoint.annotation.SoapAction;
import org.springframework.ws.soap.server.endpoint.mapping.SoapActionAnnotationMethodEndpointMapping;
/**
* @author Arjen Poutsma
*/
public class DefaultWsConfigurationTest {
private ApplicationContext applicationContext;
@Before
public void setUp() throws Exception {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(TestConfig.class);
applicationContext.refresh();
this.applicationContext = applicationContext;
}
@Test
public void payloadRootAnnotationMethodEndpointMapping() throws Exception {
PayloadRootAnnotationMethodEndpointMapping endpointMapping = this.applicationContext.getBean(
PayloadRootAnnotationMethodEndpointMapping.class);
assertEquals(0, endpointMapping.getOrder());
}
@Test
public void soapActionAnnotationMethodEndpointMapping() throws Exception {
SoapActionAnnotationMethodEndpointMapping endpointMapping = this.applicationContext.getBean(
SoapActionAnnotationMethodEndpointMapping.class);
assertEquals(1, endpointMapping.getOrder());
}
@Test
public void annotationActionEndpointMapping() throws Exception {
AnnotationActionEndpointMapping endpointMapping = this.applicationContext.getBean(
AnnotationActionEndpointMapping.class);
assertEquals(2, endpointMapping.getOrder());
}
@Test
public void defaultMethodEndpointAdapter() throws Exception {
DefaultMethodEndpointAdapter adapter =
this.applicationContext.getBean(DefaultMethodEndpointAdapter.class);
assertFalse(adapter.getMethodArgumentResolvers().isEmpty());
assertFalse(adapter.getMethodReturnValueHandlers().isEmpty());
}
@EnableWs
@Configuration
public static class TestConfig {
@Bean(name="testEndpoint")
public TestEndpoint testEndpoint() {
return new TestEndpoint();
}
}
@Endpoint
private static class TestEndpoint {
@SoapAction("handle")
public void handle() {
}
}
}

View File

@@ -0,0 +1,80 @@
package org.springframework.ws.config.annotation;
import java.util.List;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ws.server.EndpointInterceptor;
import org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter;
import org.springframework.ws.server.endpoint.interceptor.EndpointInterceptorAdapter;
import org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping;
/**
* @author Arjen Poutsma
*/
public class WsConfigurationSupportTest {
private ApplicationContext applicationContext;
@Before
public void setUp() throws Exception {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext();
applicationContext.register(TestConfig.class);
applicationContext.refresh();
this.applicationContext = applicationContext;
}
@Test
public void interceptors() {
PayloadRootAnnotationMethodEndpointMapping endpointMapping = this.applicationContext.getBean(
PayloadRootAnnotationMethodEndpointMapping.class);
assertEquals(0, endpointMapping.getOrder());
EndpointInterceptor[] interceptors = endpointMapping.getInterceptors();
assertEquals(1, interceptors.length);
assertTrue(interceptors[0] instanceof MyInterceptor);
}
@Test
public void defaultMethodEndpointAdapter() {
DefaultMethodEndpointAdapter endpointAdapter =
this.applicationContext.getBean(DefaultMethodEndpointAdapter.class);
assertNotNull(endpointAdapter);
assertTrue(endpointAdapter instanceof MyDefaultMethodEndpointAdapter);
}
@Configuration
public static class TestConfig extends WsConfigurationSupport {
@Override
protected void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(new MyInterceptor());
}
@Bean
@Override
public DefaultMethodEndpointAdapter defaultMethodEndpointAdapter() {
return new MyDefaultMethodEndpointAdapter();
}
}
public static class MyInterceptor extends EndpointInterceptorAdapter {
}
public static class MyDefaultMethodEndpointAdapter
extends DefaultMethodEndpointAdapter {
}
}

View File

@@ -0,0 +1,128 @@
package org.springframework.ws.config.annotation;
import java.util.List;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.MethodParameter;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.server.EndpointInterceptor;
import org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter;
import org.springframework.ws.server.endpoint.adapter.method.MethodArgumentResolver;
import org.springframework.ws.server.endpoint.adapter.method.MethodReturnValueHandler;
import org.springframework.ws.server.endpoint.interceptor.EndpointInterceptorAdapter;
import org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping;
/**
* @author Arjen Poutsma
*/
public class WsConfigurerAdapterTest {
private ApplicationContext applicationContext;
@Before
public void setUp() throws Exception {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(TestConfig.class);
applicationContext.refresh();
this.applicationContext = applicationContext;
}
@Test
public void interceptors() {
PayloadRootAnnotationMethodEndpointMapping endpointMapping = this.applicationContext.getBean(
PayloadRootAnnotationMethodEndpointMapping.class);
assertEquals(0, endpointMapping.getOrder());
EndpointInterceptor[] interceptors = endpointMapping.getInterceptors();
assertEquals(1, interceptors.length);
assertTrue(interceptors[0] instanceof MyInterceptor);
}
@Test
public void argumentResolvers() {
DefaultMethodEndpointAdapter endpointAdapter = this.applicationContext.getBean(DefaultMethodEndpointAdapter.class);
List<MethodArgumentResolver> argumentResolvers =
endpointAdapter.getCustomMethodArgumentResolvers();
assertEquals(1, argumentResolvers.size());
assertTrue(argumentResolvers.get(0) instanceof MyMethodArgumentResolver);
argumentResolvers = endpointAdapter.getMethodArgumentResolvers();
assertFalse(argumentResolvers.isEmpty());
}
@Test
public void returnValueHandlers() {
DefaultMethodEndpointAdapter endpointAdapter = this.applicationContext.getBean(DefaultMethodEndpointAdapter.class);
List<MethodReturnValueHandler> returnValueHandlers =
endpointAdapter.getCustomMethodReturnValueHandlers();
assertEquals(1, returnValueHandlers.size());
assertTrue(returnValueHandlers.get(0) instanceof MyReturnValueHandler);
returnValueHandlers = endpointAdapter.getMethodReturnValueHandlers();
assertFalse(returnValueHandlers.isEmpty());
}
@Configuration
@EnableWs
public static class TestConfig extends WsConfigurerAdapter {
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
interceptors.add(new MyInterceptor());
}
@Override
public void addArgumentResolvers(List<MethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new MyMethodArgumentResolver());
}
@Override
public void addReturnValueHandlers(
List<MethodReturnValueHandler> returnValueHandlers) {
returnValueHandlers.add(new MyReturnValueHandler());
}
}
public static class MyInterceptor extends EndpointInterceptorAdapter {
}
public static class MyMethodArgumentResolver implements MethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return false;
}
@Override
public Object resolveArgument(MessageContext messageContext,
MethodParameter parameter) throws Exception {
return null;
}
}
public static class MyReturnValueHandler implements MethodReturnValueHandler {
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return false;
}
@Override
public void handleReturnValue(MessageContext messageContext,
MethodParameter returnType, Object returnValue) throws Exception {
}
}
}