diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/webservices/client/WebServiceTemplateAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/webservices/client/WebServiceTemplateAutoConfiguration.java new file mode 100644 index 0000000000..d7a2edfeb2 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/webservices/client/WebServiceTemplateAutoConfiguration.java @@ -0,0 +1,67 @@ +/* + * Copyright 2012-2018 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.boot.autoconfigure.webservices.client; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.webservices.client.WebServiceTemplateBuilder; +import org.springframework.boot.webservices.client.WebServiceTemplateCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotationAwareOrderComparator; +import org.springframework.oxm.Marshaller; +import org.springframework.oxm.Unmarshaller; +import org.springframework.util.CollectionUtils; +import org.springframework.ws.client.core.WebServiceTemplate; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for {@link WebServiceTemplate}. + * + * @author Dmytro Nosan + * @since 2.1.0 + */ +@Configuration +@ConditionalOnClass({ WebServiceTemplate.class, Unmarshaller.class, Marshaller.class }) +public class WebServiceTemplateAutoConfiguration { + + private final ObjectProvider> webServiceTemplateCustomizers; + + public WebServiceTemplateAutoConfiguration( + ObjectProvider> webServiceTemplateCustomizers) { + this.webServiceTemplateCustomizers = webServiceTemplateCustomizers; + } + + @Bean + @ConditionalOnMissingBean + public WebServiceTemplateBuilder webServiceTemplateBuilder() { + WebServiceTemplateBuilder builder = new WebServiceTemplateBuilder(); + List customizers = this.webServiceTemplateCustomizers + .getIfAvailable(); + if (!CollectionUtils.isEmpty(customizers)) { + customizers = new ArrayList<>(customizers); + AnnotationAwareOrderComparator.sort(customizers); + builder = builder.customizers(customizers); + } + return builder; + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/webservices/client/package-info.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/webservices/client/package-info.java new file mode 100644 index 0000000000..5677addf45 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/webservices/client/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2018 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. + */ + +/** + * Auto-configuration for Spring Web Services Clients. + */ +package org.springframework.boot.autoconfigure.webservices.client; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index 8abd511201..69b1f0e790 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -126,7 +126,8 @@ org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\ -org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration +org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\ +org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration # Failure analyzers org.springframework.boot.diagnostics.FailureAnalyzer=\ diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/webservices/client/WebServiceTemplateAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/webservices/client/WebServiceTemplateAutoConfigurationTests.java new file mode 100644 index 0000000000..6d647e104e --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/webservices/client/WebServiceTemplateAutoConfigurationTests.java @@ -0,0 +1,166 @@ +/* + * Copyright 2012-2018 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.boot.autoconfigure.webservices.client; + +import java.util.function.Consumer; + +import org.junit.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.assertj.AssertableApplicationContext; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.boot.test.context.runner.ContextConsumer; +import org.springframework.boot.webservices.client.WebServiceTemplateBuilder; +import org.springframework.boot.webservices.client.WebServiceTemplateCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.oxm.Marshaller; +import org.springframework.oxm.Unmarshaller; +import org.springframework.oxm.jaxb.Jaxb2Marshaller; +import org.springframework.ws.client.core.WebServiceTemplate; +import org.springframework.ws.transport.WebServiceMessageSender; +import org.springframework.ws.transport.http.ClientHttpRequestMessageSender; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link WebServiceTemplateAutoConfiguration}. + * + * @author Stephane Nicoll + * @author Dmytro Nosan + */ +public class WebServiceTemplateAutoConfigurationTests { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withConfiguration( + AutoConfigurations.of(WebServiceTemplateAutoConfiguration.class)); + + @Test + public void autoConfiguredBuilderShouldNotHaveMarshallerAndUnmarshaller() { + this.contextRunner.run(assertWebServiceTemplateBuilder((builder) -> { + WebServiceTemplate webServiceTemplate = builder.build(); + assertThat(webServiceTemplate.getUnmarshaller()).isNull(); + assertThat(webServiceTemplate.getMarshaller()).isNull(); + })); + } + + @Test + public void autoConfiguredBuilderShouldHaveHttpMessageSenderByDefault() { + this.contextRunner.run(assertWebServiceTemplateBuilder((builder) -> { + WebServiceTemplate webServiceTemplate = builder.build(); + assertThat(webServiceTemplate.getMessageSenders()).hasSize(1); + WebServiceMessageSender messageSender = webServiceTemplate + .getMessageSenders()[0]; + assertThat(messageSender).isInstanceOf(ClientHttpRequestMessageSender.class); + })); + } + + @Test + public void webServiceTemplateWhenHasCustomBuilderShouldUseCustomBuilder() { + this.contextRunner + .withUserConfiguration(CustomWebServiceTemplateBuilderConfig.class) + .run(assertWebServiceTemplateBuilder((builder) -> { + WebServiceTemplate webServiceTemplate = builder.build(); + assertThat(webServiceTemplate.getMarshaller()) + .isSameAs(CustomWebServiceTemplateBuilderConfig.marshaller); + })); + } + + @Test + public void webServiceTemplateShouldApplyCustomizer() { + this.contextRunner.withUserConfiguration(WebServiceTemplateCustomizerConfig.class) + .run(assertWebServiceTemplateBuilder((builder) -> { + WebServiceTemplate webServiceTemplate = builder.build(); + assertThat(webServiceTemplate.getUnmarshaller()) + .isSameAs(WebServiceTemplateCustomizerConfig.unmarshaller); + })); + } + + @Test + public void builderShouldBeFreshForEachUse() { + this.contextRunner.withUserConfiguration(DirtyWebServiceTemplateConfig.class) + .run((context) -> { + assertThat(context).hasNotFailed(); + }); + } + + private ContextConsumer assertWebServiceTemplateBuilder( + Consumer builder) { + return (context) -> { + assertThat(context).hasSingleBean(WebServiceTemplateBuilder.class); + builder.accept(context.getBean(WebServiceTemplateBuilder.class)); + }; + } + + @Configuration + static class DirtyWebServiceTemplateConfig { + + @Bean + public WebServiceTemplate webServiceTemplateOne( + WebServiceTemplateBuilder builder) { + try { + return builder.build(); + } + finally { + breakBuilderOnNextCall(builder); + } + } + + @Bean + public WebServiceTemplate webServiceTemplateTwo( + WebServiceTemplateBuilder builder) { + try { + return builder.build(); + } + finally { + breakBuilderOnNextCall(builder); + } + } + + private void breakBuilderOnNextCall(WebServiceTemplateBuilder builder) { + builder.additionalCustomizers((webServiceTemplate) -> { + throw new IllegalStateException(); + }); + } + + } + + @Configuration + static class CustomWebServiceTemplateBuilderConfig { + + private static final Marshaller marshaller = new Jaxb2Marshaller(); + + @Bean + public WebServiceTemplateBuilder webServiceTemplateBuilder() { + return new WebServiceTemplateBuilder().setMarshaller(marshaller); + } + + } + + @Configuration + static class WebServiceTemplateCustomizerConfig { + + private static final Unmarshaller unmarshaller = new Jaxb2Marshaller(); + + @Bean + public WebServiceTemplateCustomizer webServiceTemplateCustomizer() { + return (ws) -> ws.setUnmarshaller(unmarshaller); + } + + } + +} diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index d0677cafd9..ed40a6892e 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -7388,6 +7388,51 @@ following example: +[[boot-features-webservices-template]] +== Calling Web Services with `WebServiceTemplate` +If you need to call remote Web services from your application, you can use the +{spring-webservices-reference}#client-web-service-template[`WebServiceTemplate`] class. +Since `WebServiceTemplate` instances often need to be customized before being used, Spring +Boot does not provide any single auto-configured `WebServiceTemplate` bean. It does, +however, auto-configure a `WebServiceTemplateBuilder`, which can be used to create +`WebServiceTemplate` instances when needed. + +The following code shows a typical example: + +[source,java,indent=0] +---- + @Service + public class MyService { + + private final WebServiceTemplate webServiceTemplate; + + public MyService(WebServiceTemplateBuilder webServiceTemplateBuilder) { + this.webServiceTemplate = webServiceTemplateBuilder.build(); + } + + public DetailsResp someWsCall(DetailsReq detailsReq) { + return (DetailsResp) this.webServiceTemplate.marshalSendAndReceive(detailsReq, new SoapActionCallback(ACTION)); + + } + + } +---- + +By default, `WebServiceTemplateBuilder` detects a suitable HTTP-based +`WebServiceMessageSender` using the available HTTP client libraries on the classpath. You +can also customize read and connection timeouts as follows: + +[source,java,indent=0] +---- + @Bean + public WebServiceTemplate webServiceTemplate(WebServiceTemplateBuilder builder) { + return builder.messageSenders(new HttpWebServiceMessageSenderBuilder() + .setReadTimeout(5000).setConnectionTimeout(2000).build()).build(); + } +---- + + + [[boot-features-developing-auto-configuration]] == Creating Your Own Auto-configuration If you work in a company that develops shared libraries, or if you work on an open-source diff --git a/spring-boot-project/spring-boot/pom.xml b/spring-boot-project/spring-boot/pom.xml index aff395bf3f..b268584e08 100644 --- a/spring-boot-project/spring-boot/pom.xml +++ b/spring-boot-project/spring-boot/pom.xml @@ -245,6 +245,11 @@ spring-orm true + + org.springframework + spring-oxm + true + org.springframework spring-test @@ -270,6 +275,11 @@ spring-security-web true + + org.springframework.ws + spring-ws-core + true + org.yaml snakeyaml diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/ClientHttpRequestFactorySupplier.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/ClientHttpRequestFactorySupplier.java new file mode 100644 index 0000000000..f55039f233 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/ClientHttpRequestFactorySupplier.java @@ -0,0 +1,65 @@ +/* + * Copyright 2012-2018 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.boot.web.client; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.Supplier; + +import org.springframework.beans.BeanUtils; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.util.ClassUtils; + +/** + * A supplier for {@link ClientHttpRequestFactory} that detects the preferred candidate + * based on the available implementations on the classpath. + * + * @author Stephane Nicoll + * @since 2.1.0 + */ +public class ClientHttpRequestFactorySupplier + implements Supplier { + + private static final Map REQUEST_FACTORY_CANDIDATES; + + static { + Map candidates = new LinkedHashMap<>(); + candidates.put("org.apache.http.client.HttpClient", + "org.springframework.http.client.HttpComponentsClientHttpRequestFactory"); + candidates.put("okhttp3.OkHttpClient", + "org.springframework.http.client.OkHttp3ClientHttpRequestFactory"); + REQUEST_FACTORY_CANDIDATES = Collections.unmodifiableMap(candidates); + } + + @Override + public ClientHttpRequestFactory get() { + for (Map.Entry candidate : REQUEST_FACTORY_CANDIDATES + .entrySet()) { + ClassLoader classLoader = getClass().getClassLoader(); + if (ClassUtils.isPresent(candidate.getKey(), classLoader)) { + Class factoryClass = ClassUtils.resolveClassName(candidate.getValue(), + classLoader); + return (ClientHttpRequestFactory) BeanUtils + .instantiateClass(factoryClass); + } + } + return new SimpleClientHttpRequestFactory(); + } + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/RestTemplateBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/RestTemplateBuilder.java index 58d464deb4..5038a5ecd0 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/RestTemplateBuilder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/client/RestTemplateBuilder.java @@ -23,9 +23,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.LinkedHashSet; -import java.util.Map; import java.util.Set; import java.util.function.Supplier; @@ -33,11 +31,9 @@ import org.springframework.beans.BeanUtils; import org.springframework.http.client.AbstractClientHttpRequestFactoryWrapper; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequestInterceptor; -import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.http.client.support.BasicAuthorizationInterceptor; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; import org.springframework.util.ReflectionUtils; import org.springframework.web.client.ResponseErrorHandler; @@ -64,17 +60,6 @@ import org.springframework.web.util.UriTemplateHandler; */ public class RestTemplateBuilder { - private static final Map REQUEST_FACTORY_CANDIDATES; - - static { - Map candidates = new LinkedHashMap<>(); - candidates.put("org.apache.http.client.HttpClient", - "org.springframework.http.client.HttpComponentsClientHttpRequestFactory"); - candidates.put("okhttp3.OkHttpClient", - "org.springframework.http.client.OkHttp3ClientHttpRequestFactory"); - REQUEST_FACTORY_CANDIDATES = Collections.unmodifiableMap(candidates); - } - private final boolean detectRequestFactory; private final String rootUri; @@ -561,7 +546,7 @@ public class RestTemplateBuilder { requestFactory = this.requestFactorySupplier.get(); } else if (this.detectRequestFactory) { - requestFactory = detectRequestFactory(); + requestFactory = new ClientHttpRequestFactorySupplier().get(); } if (requestFactory != null) { ClientHttpRequestFactory unwrappedRequestFactory = unwrapRequestFactoryIfNecessary( @@ -590,20 +575,6 @@ public class RestTemplateBuilder { return unwrappedRequestFactory; } - private ClientHttpRequestFactory detectRequestFactory() { - for (Map.Entry candidate : REQUEST_FACTORY_CANDIDATES - .entrySet()) { - ClassLoader classLoader = getClass().getClassLoader(); - if (ClassUtils.isPresent(candidate.getKey(), classLoader)) { - Class factoryClass = ClassUtils.resolveClassName(candidate.getValue(), - classLoader); - return (ClientHttpRequestFactory) BeanUtils - .instantiateClass(factoryClass); - } - } - return new SimpleClientHttpRequestFactory(); - } - private Set append(Set set, T addition) { Set result = new LinkedHashSet<>(set != null ? set : Collections.emptySet()); result.add(addition); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/webservices/client/HttpWebServiceMessageSenderBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/webservices/client/HttpWebServiceMessageSenderBuilder.java new file mode 100644 index 0000000000..e5c998b246 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/webservices/client/HttpWebServiceMessageSenderBuilder.java @@ -0,0 +1,124 @@ +/* + * Copyright 2012-2018 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.boot.webservices.client; + +import java.lang.reflect.Method; +import java.util.function.Supplier; + +import org.springframework.boot.web.client.ClientHttpRequestFactorySupplier; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; +import org.springframework.ws.transport.WebServiceMessageSender; +import org.springframework.ws.transport.http.ClientHttpRequestMessageSender; + +/** + * {@link WebServiceMessageSender} builder that can detect a suitable HTTP library based + * on the classpath. + * + * @author Stephane Nicoll + * @since 2.1.0 + */ +public class HttpWebServiceMessageSenderBuilder { + + private Integer connectionTimeout; + + private Integer readTimeout; + + private Supplier requestFactorySupplier; + + /** + * Set the connection timeout in milliseconds. + * @param connectionTimeout the connection timeout in milliseconds + * @return a new builder instance + */ + public HttpWebServiceMessageSenderBuilder setConnectionTimeout( + int connectionTimeout) { + this.connectionTimeout = connectionTimeout; + return this; + } + + /** + * Set the read timeout in milliseconds. + * @param readTimeout the read timeout in milliseconds + * @return a new builder instance + */ + public HttpWebServiceMessageSenderBuilder setReadTimeout(int readTimeout) { + this.readTimeout = readTimeout; + return this; + } + + /** + * Set the {@code Supplier} of {@link ClientHttpRequestFactory} that should be called + * to create the HTTP-based {@link WebServiceMessageSender}. + * @param requestFactorySupplier the supplier for the request factory + * @return a new builder instance + */ + public HttpWebServiceMessageSenderBuilder requestFactory( + Supplier requestFactorySupplier) { + Assert.notNull(requestFactorySupplier, + "RequestFactory Supplier must not be null"); + this.requestFactorySupplier = requestFactorySupplier; + return this; + } + + public WebServiceMessageSender build() { + ClientHttpRequestFactory requestFactory = (this.requestFactorySupplier != null + ? this.requestFactorySupplier.get() + : new ClientHttpRequestFactorySupplier().get()); + if (this.connectionTimeout != null) { + new TimeoutRequestFactoryCustomizer(this.connectionTimeout, + "setConnectTimeout").customize(requestFactory); + } + if (this.readTimeout != null) { + new TimeoutRequestFactoryCustomizer(this.readTimeout, "setReadTimeout") + .customize(requestFactory); + } + return new ClientHttpRequestMessageSender(requestFactory); + } + + /** + * {@link ClientHttpRequestFactory} customizer to call a "set timeout" method. + */ + private static class TimeoutRequestFactoryCustomizer { + + private final int timeout; + + private final String methodName; + + TimeoutRequestFactoryCustomizer(int timeout, String methodName) { + this.timeout = timeout; + this.methodName = methodName; + } + + public void customize(ClientHttpRequestFactory factory) { + ReflectionUtils.invokeMethod(findMethod(factory), factory, this.timeout); + } + + private Method findMethod(ClientHttpRequestFactory factory) { + Method method = ReflectionUtils.findMethod(factory.getClass(), + this.methodName, int.class); + if (method != null) { + return method; + } + throw new IllegalStateException("Request factory " + factory.getClass() + + " does not have a " + this.methodName + "(int) method"); + } + + } + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilder.java new file mode 100644 index 0000000000..c8d831c3fb --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilder.java @@ -0,0 +1,665 @@ +/* + * Copyright 2012-2018 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.boot.webservices.client; + +import java.net.URI; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +import javax.xml.transform.TransformerFactory; + +import org.springframework.beans.BeanUtils; +import org.springframework.oxm.Marshaller; +import org.springframework.oxm.Unmarshaller; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.ws.WebServiceMessageFactory; +import org.springframework.ws.client.core.FaultMessageResolver; +import org.springframework.ws.client.core.WebServiceTemplate; +import org.springframework.ws.client.support.destination.DestinationProvider; +import org.springframework.ws.client.support.interceptor.ClientInterceptor; +import org.springframework.ws.transport.WebServiceMessageSender; + +/** + * Builder that can be used to configure and create a {@link WebServiceTemplate}. Provides + * convenience methods to register {@link #messageSenders(WebServiceMessageSender...) + * message senders}, {@link #interceptors(ClientInterceptor...) client interceptors} and + * {@link #customizers(WebServiceTemplateCustomizer...) customizers}. + *

+ * By default the built {@link WebServiceTemplate} uses the most suitable HTTP-based + * {@link WebServiceMessageSender}, call {@link #detectHttpMessageSender(boolean) + * detectHttpMessageSender(false)} if you prefer to keep the default. In a typical + * auto-configured Spring Boot application this builder is available as a bean and can be + * injected whenever a {@link WebServiceTemplate} is needed. + * + * @author Dmytro Nosan + * @author Stephane Nicoll + * @since 2.1.0 + */ +public class WebServiceTemplateBuilder { + + private final boolean detectHttpMessageSender; + + private final Set interceptors; + + private final Set internalCustomizers; + + private final Set customizers; + + private final WebServiceMessageSenders messageSenders; + + private final Marshaller marshaller; + + private final Unmarshaller unmarshaller; + + private final DestinationProvider destinationProvider; + + private final Class transformerFactoryClass; + + private final WebServiceMessageFactory messageFactory; + + public WebServiceTemplateBuilder(WebServiceTemplateCustomizer... customizers) { + this(true, Collections.emptySet(), Collections.emptySet(), + append(Collections.emptySet(), customizers), + new WebServiceMessageSenders(), null, null, null, null, null); + } + + private WebServiceTemplateBuilder(boolean detectHttpMessageSender, + Set interceptors, + Set internalCustomizers, + Set customizers, + WebServiceMessageSenders messageSenders, Marshaller marshaller, + Unmarshaller unmarshaller, DestinationProvider destinationProvider, + Class transformerFactoryClass, + WebServiceMessageFactory messageFactory) { + this.interceptors = interceptors; + this.internalCustomizers = internalCustomizers; + this.customizers = customizers; + this.messageSenders = messageSenders; + this.detectHttpMessageSender = detectHttpMessageSender; + this.marshaller = marshaller; + this.unmarshaller = unmarshaller; + this.destinationProvider = destinationProvider; + this.transformerFactoryClass = transformerFactoryClass; + this.messageFactory = messageFactory; + } + + /** + * Set if a suitable HTTP-based {@link WebServiceMessageSender} should be detected + * based on the classpath. Default is {@code true}. + * @param detectHttpMessageSender if a HTTP-based {@link WebServiceMessageSender} + * should be detected + * @return a new builder instance + * @see HttpWebServiceMessageSenderBuilder + */ + public WebServiceTemplateBuilder detectHttpMessageSender( + boolean detectHttpMessageSender) { + return new WebServiceTemplateBuilder(detectHttpMessageSender, this.interceptors, + this.internalCustomizers, this.customizers, this.messageSenders, + this.marshaller, this.unmarshaller, this.destinationProvider, + this.transformerFactoryClass, this.messageFactory); + } + + /** + * Sets the {@link WebServiceMessageSender WebServiceMessageSenders} that should be + * used with the {@link WebServiceTemplate}. Setting this value will replace any + * previously defined message senders, including the HTTP-based message sender, if + * any. Consider using {@link #additionalMessageSenders(WebServiceMessageSender...)} + * to keep it with user-defined message senders. + * @param messageSenders the message senders to set + * @return a new builder instance. + * @see #additionalMessageSenders(WebServiceMessageSender...) + * @see #detectHttpMessageSender(boolean) + */ + public WebServiceTemplateBuilder messageSenders( + WebServiceMessageSender... messageSenders) { + Assert.notNull(messageSenders, "MessageSenders must not be null"); + return messageSenders(Arrays.asList(messageSenders)); + } + + /** + * Sets the {@link WebServiceMessageSender WebServiceMessageSenders} that should be + * used with the {@link WebServiceTemplate}. Setting this value will replace any + * previously defined message senders, including the HTTP-based message sender, if + * any. Consider using {@link #additionalMessageSenders(Collection)} to keep it with + * user-defined message senders. + * @param messageSenders the message senders to set + * @return a new builder instance. + * @see #additionalMessageSenders(Collection) + * @see #detectHttpMessageSender(boolean) + */ + public WebServiceTemplateBuilder messageSenders( + Collection messageSenders) { + Assert.notNull(messageSenders, "MessageSenders must not be null"); + return new WebServiceTemplateBuilder(this.detectHttpMessageSender, + this.interceptors, this.internalCustomizers, this.customizers, + this.messageSenders.set(messageSenders), this.marshaller, + this.unmarshaller, this.destinationProvider, this.transformerFactoryClass, + this.messageFactory); + } + + /** + * Add additional {@link WebServiceMessageSender WebServiceMessageSenders} that should + * be used with the {@link WebServiceTemplate}. + * @param messageSenders the message senders to add + * @return a new builder instance. + * @see #messageSenders(WebServiceMessageSender...) + */ + public WebServiceTemplateBuilder additionalMessageSenders( + WebServiceMessageSender... messageSenders) { + Assert.notNull(messageSenders, "MessageSenders must not be null"); + return additionalMessageSenders(Arrays.asList(messageSenders)); + } + + /** + * Add additional {@link WebServiceMessageSender WebServiceMessageSenders} that should + * be used with the {@link WebServiceTemplate}. + * @param messageSenders the message senders to add + * @return a new builder instance. + * @see #messageSenders(Collection) + */ + public WebServiceTemplateBuilder additionalMessageSenders( + Collection messageSenders) { + Assert.notNull(messageSenders, "MessageSenders must not be null"); + return new WebServiceTemplateBuilder(this.detectHttpMessageSender, + this.interceptors, this.internalCustomizers, this.customizers, + this.messageSenders.add(messageSenders), this.marshaller, + this.unmarshaller, this.destinationProvider, this.transformerFactoryClass, + this.messageFactory); + } + + /** + * Set the {@link ClientInterceptor ClientInterceptors} that should be used with the + * {@link WebServiceTemplate}. Setting this value will replace any previously defined + * interceptors. + * @param interceptors the interceptors to set + * @return a new builder instance + * @see #additionalInterceptors(ClientInterceptor...) + */ + public WebServiceTemplateBuilder interceptors(ClientInterceptor... interceptors) { + Assert.notNull(interceptors, "Interceptors must not be null"); + return interceptors(Arrays.asList(interceptors)); + } + + /** + * Set the {@link ClientInterceptor ClientInterceptors} that should be used with the + * {@link WebServiceTemplate}. Setting this value will replace any previously defined + * interceptors. + * @param interceptors the interceptors to set + * @return a new builder instance + * @see #additionalInterceptors(Collection) + */ + public WebServiceTemplateBuilder interceptors( + Collection interceptors) { + Assert.notNull(interceptors, "Interceptors must not be null"); + return new WebServiceTemplateBuilder(this.detectHttpMessageSender, + append(Collections.emptySet(), interceptors), + this.internalCustomizers, this.customizers, this.messageSenders, + this.marshaller, this.unmarshaller, this.destinationProvider, + this.transformerFactoryClass, this.messageFactory); + } + + /** + * Add additional {@link ClientInterceptor ClientInterceptors} that should be used + * with the {@link WebServiceTemplate}. + * @param interceptors the interceptors to add + * @return a new builder instance + * @see #interceptors(ClientInterceptor...) + */ + public WebServiceTemplateBuilder additionalInterceptors( + ClientInterceptor... interceptors) { + Assert.notNull(interceptors, "Interceptors must not be null"); + return additionalInterceptors(Arrays.asList(interceptors)); + } + + /** + * Add additional {@link ClientInterceptor ClientInterceptors} that should be used + * with the {@link WebServiceTemplate}. + * @param interceptors the interceptors to add + * @return a new builder instance + * @see #interceptors(Collection) + */ + public WebServiceTemplateBuilder additionalInterceptors( + Collection interceptors) { + Assert.notNull(interceptors, "Interceptors must not be null"); + return new WebServiceTemplateBuilder(this.detectHttpMessageSender, + append(this.interceptors, interceptors), this.internalCustomizers, + this.customizers, this.messageSenders, this.marshaller, this.unmarshaller, + this.destinationProvider, this.transformerFactoryClass, + this.messageFactory); + } + + /** + * Set {@link WebServiceTemplateCustomizer WebServiceTemplateCustomizers} that should + * be applied to the {@link WebServiceTemplate}. Customizers are applied in the order + * that they were added after builder configuration has been applied. Setting this + * value will replace any previously configured customizers. + * @param customizers the customizers to set + * @return a new builder instance + * @see #additionalCustomizers(WebServiceTemplateCustomizer...) + */ + public WebServiceTemplateBuilder customizers( + WebServiceTemplateCustomizer... customizers) { + Assert.notNull(customizers, "Customizers must not be null"); + return customizers(Arrays.asList(customizers)); + } + + /** + * Set {@link WebServiceTemplateCustomizer WebServiceTemplateCustomizers} that should + * be applied to the {@link WebServiceTemplate}. Customizers are applied in the order + * that they were added after builder configuration has been applied. Setting this + * value will replace any previously configured customizers. + * @param customizers the customizers to set + * @return a new builder instance + * @see #additionalCustomizers(Collection) + */ + public WebServiceTemplateBuilder customizers( + Collection customizers) { + Assert.notNull(customizers, "Customizers must not be null"); + return new WebServiceTemplateBuilder(this.detectHttpMessageSender, + this.interceptors, this.internalCustomizers, + append(Collections.emptySet(), customizers), + this.messageSenders, this.marshaller, this.unmarshaller, + this.destinationProvider, this.transformerFactoryClass, + this.messageFactory); + } + + /** + * Add additional {@link WebServiceTemplateCustomizer WebServiceTemplateCustomizers} + * that should be applied to the {@link WebServiceTemplate}. Customizers are applied + * in the order that they were added after builder configuration has been applied. + * @param customizers the customizers to add + * @return a new builder instance + * @see #customizers(WebServiceTemplateCustomizer...) + */ + public WebServiceTemplateBuilder additionalCustomizers( + WebServiceTemplateCustomizer... customizers) { + Assert.notNull(customizers, "Customizers must not be null"); + return additionalCustomizers(Arrays.asList(customizers)); + } + + /** + * Add additional {@link WebServiceTemplateCustomizer WebServiceTemplateCustomizers} + * that should be applied to the {@link WebServiceTemplate}. Customizers are applied + * in the order that they were added after builder configuration has been applied. + * @param customizers the customizers to add + * @return a new builder instance + * @see #customizers(Collection) + */ + public WebServiceTemplateBuilder additionalCustomizers( + Collection customizers) { + Assert.notNull(customizers, "Customizers must not be null"); + return new WebServiceTemplateBuilder(this.detectHttpMessageSender, + this.interceptors, this.internalCustomizers, + append(this.customizers, customizers), this.messageSenders, + this.marshaller, this.unmarshaller, this.destinationProvider, + this.transformerFactoryClass, this.messageFactory); + } + + /** + * Indicates whether the connection should be checked for fault indicators + * ({@code true}), or whether we should rely on the message only ({@code false}). + * @param checkConnectionForFault whether to check for fault indicators + * @return a new builder instance. + * @see WebServiceTemplate#setCheckConnectionForFault(boolean) + */ + public WebServiceTemplateBuilder setCheckConnectionForFault( + boolean checkConnectionForFault) { + return new WebServiceTemplateBuilder(this.detectHttpMessageSender, + this.interceptors, + append(this.internalCustomizers, + new CheckConnectionFaultCustomizer(checkConnectionForFault)), + this.customizers, this.messageSenders, this.marshaller, this.unmarshaller, + this.destinationProvider, this.transformerFactoryClass, + this.messageFactory); + } + + /** + * Indicates whether the connection should be checked for error indicators + * ({@code true}), or whether these should be ignored ({@code false}). + * @param checkConnectionForError whether to check for error indicators + * @return a new builder instance. + * @see WebServiceTemplate#setCheckConnectionForError(boolean) + */ + public WebServiceTemplateBuilder setCheckConnectionForError( + boolean checkConnectionForError) { + return new WebServiceTemplateBuilder(this.detectHttpMessageSender, + this.interceptors, + append(this.internalCustomizers, + new CheckConnectionForErrorCustomizer(checkConnectionForError)), + this.customizers, this.messageSenders, this.marshaller, this.unmarshaller, + this.destinationProvider, this.transformerFactoryClass, + this.messageFactory); + } + + /** + * Sets the {@link WebServiceMessageFactory} to use for creating messages. + * @param messageFactory the message factory to use for creating messages + * @return a new builder instance. + * @see WebServiceTemplate#setMessageFactory(WebServiceMessageFactory) + **/ + public WebServiceTemplateBuilder setWebServiceMessageFactory( + WebServiceMessageFactory messageFactory) { + Assert.notNull(messageFactory, "messageFactory must not be null"); + return new WebServiceTemplateBuilder(this.detectHttpMessageSender, + this.interceptors, this.internalCustomizers, this.customizers, + this.messageSenders, this.marshaller, this.unmarshaller, + this.destinationProvider, this.transformerFactoryClass, messageFactory); + } + + /** + * Set the {@link Unmarshaller} to use to deserialize messages. + * @param unmarshaller the message unmarshaller + * @return a new builder instance. + * @see WebServiceTemplate#setUnmarshaller(Unmarshaller) + **/ + public WebServiceTemplateBuilder setUnmarshaller(Unmarshaller unmarshaller) { + return new WebServiceTemplateBuilder(this.detectHttpMessageSender, + this.interceptors, this.internalCustomizers, this.customizers, + this.messageSenders, this.marshaller, unmarshaller, + this.destinationProvider, this.transformerFactoryClass, + this.messageFactory); + } + + /** + * Set the {@link Marshaller} to use to serialize messages. + * @param marshaller the message marshaller + * @return a new builder instance. + * @see WebServiceTemplate#setMarshaller(Marshaller) + **/ + public WebServiceTemplateBuilder setMarshaller(Marshaller marshaller) { + return new WebServiceTemplateBuilder(this.detectHttpMessageSender, + this.interceptors, this.internalCustomizers, this.customizers, + this.messageSenders, marshaller, this.unmarshaller, + this.destinationProvider, this.transformerFactoryClass, + this.messageFactory); + } + + /** + * Set the {@link FaultMessageResolver} to use. + * @param faultMessageResolver the fault message resolver to use + * @return a new builder instance. + * @see WebServiceTemplate#setFaultMessageResolver(FaultMessageResolver) + */ + public WebServiceTemplateBuilder setFaultMessageResolver( + FaultMessageResolver faultMessageResolver) { + return new WebServiceTemplateBuilder(this.detectHttpMessageSender, + this.interceptors, + append(this.internalCustomizers, + new FaultMessageResolverCustomizer(faultMessageResolver)), + this.customizers, this.messageSenders, this.marshaller, this.unmarshaller, + this.destinationProvider, this.transformerFactoryClass, + this.messageFactory); + } + + /** + * Set the {@link TransformerFactory} implementation to use. + * @param transformerFactoryClass the transformer factory implementation to use + * @return a new builder instance. + * @see WebServiceTemplate#setTransformerFactoryClass(Class) + */ + public WebServiceTemplateBuilder setTransformerFactoryClass( + Class transformerFactoryClass) { + return new WebServiceTemplateBuilder(this.detectHttpMessageSender, + this.interceptors, this.internalCustomizers, this.customizers, + this.messageSenders, this.marshaller, this.unmarshaller, + this.destinationProvider, transformerFactoryClass, this.messageFactory); + } + + /** + * Set the default URI to be used on operations that do not have a URI parameter. + *

+ * Typically, either this property is set, or + * {@link #setDestinationProvider(DestinationProvider)}, but not both. + * @param defaultUri the destination provider URI to be used on operations that do not + * have a URI parameter. + * @return a new builder instance. + * @see #setDestinationProvider(DestinationProvider) + */ + public WebServiceTemplateBuilder setDefaultUri(String defaultUri) { + Assert.hasText(defaultUri, "DefaultUri must not be empty"); + return setDestinationProvider(() -> URI.create(defaultUri)); + } + + /** + * Set the {@link DestinationProvider} to use + *

+ * Typically, either this property is set, or {@link #setDefaultUri(String)}, but not + * both. + * @param destinationProvider the destination provider to be used on operations that + * do not have a URI parameter. + * @return a new builder instance. + * @see WebServiceTemplate#setDestinationProvider(DestinationProvider) + */ + public WebServiceTemplateBuilder setDestinationProvider( + DestinationProvider destinationProvider) { + Assert.notNull(destinationProvider, "destinationProvider must not be null"); + return new WebServiceTemplateBuilder(this.detectHttpMessageSender, + this.interceptors, this.internalCustomizers, this.customizers, + this.messageSenders, this.marshaller, this.unmarshaller, + destinationProvider, this.transformerFactoryClass, this.messageFactory); + } + + /** + * Build a new {@link WebServiceTemplate} instance and configure it using this + * builder. + * @return a configured {@link WebServiceTemplate} instance. + * @see #build(Class) + * @see #configure(WebServiceTemplate) + */ + public WebServiceTemplate build() { + return build(WebServiceTemplate.class); + } + + /** + * Build a new {@link WebServiceTemplate} instance of the specified type and configure + * it using this builder. + * @param the type of web service template + * @param webServiceTemplateClass the template type to create + * @return a configured {@link WebServiceTemplate} instance. + * @see WebServiceTemplateBuilder#build() + * @see #configure(WebServiceTemplate) + */ + public T build(Class webServiceTemplateClass) { + Assert.notNull(webServiceTemplateClass, + "WebServiceTemplateClass must not be null"); + return configure(BeanUtils.instantiateClass(webServiceTemplateClass)); + } + + /** + * Configure the provided {@link WebServiceTemplate} instance using this builder. + * @param the type of web service template + * @param webServiceTemplate the {@link WebServiceTemplate} to configure + * @return the web service template instance + * @see #build() + * @see #build(Class) + */ + public T configure(T webServiceTemplate) { + Assert.notNull(webServiceTemplate, "webServiceTemplate must not be null"); + configureMessageSenders(webServiceTemplate); + if (!CollectionUtils.isEmpty(this.internalCustomizers)) { + for (WebServiceTemplateCustomizer internalCustomizer : this.internalCustomizers) { + internalCustomizer.customize(webServiceTemplate); + } + } + if (this.marshaller != null) { + webServiceTemplate.setMarshaller(this.marshaller); + } + if (this.unmarshaller != null) { + webServiceTemplate.setUnmarshaller(this.unmarshaller); + } + if (this.destinationProvider != null) { + webServiceTemplate.setDestinationProvider(this.destinationProvider); + } + if (this.transformerFactoryClass != null) { + webServiceTemplate.setTransformerFactoryClass(this.transformerFactoryClass); + } + if (this.messageFactory != null) { + webServiceTemplate.setMessageFactory(this.messageFactory); + } + if (!CollectionUtils.isEmpty(this.interceptors)) { + webServiceTemplate.setInterceptors( + append(this.interceptors, webServiceTemplate.getInterceptors()) + .toArray(new ClientInterceptor[0])); + } + if (!CollectionUtils.isEmpty(this.customizers)) { + for (WebServiceTemplateCustomizer customizer : this.customizers) { + customizer.customize(webServiceTemplate); + } + } + return webServiceTemplate; + } + + private void configureMessageSenders( + T webServiceTemplate) { + if (this.messageSenders.isOnlyAdditional() && this.detectHttpMessageSender) { + Set mergedMessageSenders = append( + this.messageSenders.getMessageSenders(), + new HttpWebServiceMessageSenderBuilder().build()); + webServiceTemplate.setMessageSenders( + mergedMessageSenders.toArray(new WebServiceMessageSender[0])); + } + else if (!CollectionUtils.isEmpty(this.messageSenders.getMessageSenders())) { + webServiceTemplate.setMessageSenders(this.messageSenders.getMessageSenders() + .toArray(new WebServiceMessageSender[0])); + } + } + + private static Set append(Set set, T[] additions) { + return append(set, additions != null + ? new LinkedHashSet<>(Arrays.asList(additions)) : Collections.emptySet()); + } + + private static Set append(Set set, T addition) { + Set result = new LinkedHashSet<>(set != null ? set : Collections.emptySet()); + result.add(addition); + return Collections.unmodifiableSet(result); + } + + private static Set append(Set set, Collection additions) { + Set result = new LinkedHashSet<>(set != null ? set : Collections.emptySet()); + result.addAll(additions != null ? additions : Collections.emptyList()); + return Collections.unmodifiableSet(result); + } + + /** + * Collect user-defined {@link WebServiceMessageSender} and whether only additional + * message senders were added or not. + */ + private static class WebServiceMessageSenders { + + private final boolean onlyAdditional; + + private Set messageSenders; + + WebServiceMessageSenders() { + this(true, Collections.emptySet()); + } + + private WebServiceMessageSenders(boolean onlyAdditional, + Set messageSenders) { + this.onlyAdditional = onlyAdditional; + this.messageSenders = messageSenders; + } + + public boolean isOnlyAdditional() { + return this.onlyAdditional; + } + + public Set getMessageSenders() { + return this.messageSenders; + } + + public WebServiceMessageSenders set( + Collection messageSenders) { + return new WebServiceMessageSenders(false, + new LinkedHashSet<>(messageSenders)); + } + + public WebServiceMessageSenders add( + Collection messageSenders) { + return new WebServiceMessageSenders(this.onlyAdditional, + append(this.messageSenders, messageSenders)); + } + + } + + /** + * {@link WebServiceTemplateCustomizer} to set + * {@link WebServiceTemplate#checkConnectionForFault checkConnectionForFault }. + */ + private static final class CheckConnectionFaultCustomizer + implements WebServiceTemplateCustomizer { + + private final boolean checkConnectionFault; + + private CheckConnectionFaultCustomizer(boolean checkConnectionFault) { + this.checkConnectionFault = checkConnectionFault; + } + + @Override + public void customize(WebServiceTemplate webServiceTemplate) { + webServiceTemplate.setCheckConnectionForFault(this.checkConnectionFault); + } + + } + + /** + * {@link WebServiceTemplateCustomizer} to set + * {@link WebServiceTemplate#checkConnectionForError checkConnectionForError }. + */ + private static final class CheckConnectionForErrorCustomizer + implements WebServiceTemplateCustomizer { + + private final boolean checkConnectionForError; + + private CheckConnectionForErrorCustomizer(boolean checkConnectionForError) { + this.checkConnectionForError = checkConnectionForError; + } + + @Override + public void customize(WebServiceTemplate webServiceTemplate) { + webServiceTemplate.setCheckConnectionForError(this.checkConnectionForError); + } + + } + + /** + * {@link WebServiceTemplateCustomizer} to set + * {@link WebServiceTemplate#faultMessageResolver faultMessageResolver }. + */ + private static final class FaultMessageResolverCustomizer + implements WebServiceTemplateCustomizer { + + private final FaultMessageResolver faultMessageResolver; + + private FaultMessageResolverCustomizer( + FaultMessageResolver faultMessageResolver) { + this.faultMessageResolver = faultMessageResolver; + } + + @Override + public void customize(WebServiceTemplate webServiceTemplate) { + webServiceTemplate.setFaultMessageResolver(this.faultMessageResolver); + } + + } + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/webservices/client/WebServiceTemplateCustomizer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/webservices/client/WebServiceTemplateCustomizer.java new file mode 100644 index 0000000000..36a6404ea6 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/webservices/client/WebServiceTemplateCustomizer.java @@ -0,0 +1,35 @@ +/* + * Copyright 2012-2018 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.boot.webservices.client; + +import org.springframework.ws.client.core.WebServiceTemplate; + +/** + * Callback interface that can be used to customize a {@link WebServiceTemplate}. + * + * @author Dmytro Nosan + * @since 2.1.0 + */ +public interface WebServiceTemplateCustomizer { + + /** + * Callback to customize a {@link WebServiceTemplate} instance. + * @param webServiceTemplate the template to customize + */ + void customize(WebServiceTemplate webServiceTemplate); + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/webservices/client/package-info.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/webservices/client/package-info.java new file mode 100644 index 0000000000..220d07076f --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/webservices/client/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2018 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. + */ + +/** + * Web Services client utilities. + */ +package org.springframework.boot.webservices.client; diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/HttpWebServiceMessageSenderBuilderOkHttp3IntegrationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/HttpWebServiceMessageSenderBuilderOkHttp3IntegrationTests.java new file mode 100644 index 0000000000..3edea2ae6d --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/HttpWebServiceMessageSenderBuilderOkHttp3IntegrationTests.java @@ -0,0 +1,73 @@ +/* + * Copyright 2012-2018 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.boot.webservices.client; + +import okhttp3.OkHttpClient; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.boot.testsupport.runner.classpath.ClassPathExclusions; +import org.springframework.boot.testsupport.runner.classpath.ModifiedClassPathRunner; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.OkHttp3ClientHttpRequestFactory; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.ws.transport.WebServiceMessageSender; +import org.springframework.ws.transport.http.ClientHttpRequestMessageSender; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link HttpWebServiceMessageSenderBuilder} when Http Components is not + * available. + * + * @author Stephane Nicoll + */ +@RunWith(ModifiedClassPathRunner.class) +@ClassPathExclusions("httpclient-*.jar") +public class HttpWebServiceMessageSenderBuilderOkHttp3IntegrationTests { + + private final HttpWebServiceMessageSenderBuilder builder = new HttpWebServiceMessageSenderBuilder(); + + @Test + public void buildUseOkHttp3ByDefault() { + WebServiceMessageSender messageSender = this.builder.build(); + assertOkHttp3RequestFactory(messageSender); + } + + @Test + public void buildWithCustomTimeouts() { + WebServiceMessageSender messageSender = this.builder.setConnectionTimeout(5000) + .setReadTimeout(2000).build(); + OkHttp3ClientHttpRequestFactory factory = assertOkHttp3RequestFactory( + messageSender); + OkHttpClient client = (OkHttpClient) ReflectionTestUtils.getField(factory, + "client"); + assertThat(client).isNotNull(); + assertThat(client.connectTimeoutMillis()).isEqualTo(5000); + assertThat(client.readTimeoutMillis()).isEqualTo(2000); + } + + private OkHttp3ClientHttpRequestFactory assertOkHttp3RequestFactory( + WebServiceMessageSender messageSender) { + assertThat(messageSender).isInstanceOf(ClientHttpRequestMessageSender.class); + ClientHttpRequestMessageSender sender = (ClientHttpRequestMessageSender) messageSender; + ClientHttpRequestFactory requestFactory = sender.getRequestFactory(); + assertThat(requestFactory).isInstanceOf(OkHttp3ClientHttpRequestFactory.class); + return (OkHttp3ClientHttpRequestFactory) requestFactory; + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/HttpWebServiceMessageSenderBuilderSimpleIntegrationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/HttpWebServiceMessageSenderBuilderSimpleIntegrationTests.java new file mode 100644 index 0000000000..361927ff29 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/HttpWebServiceMessageSenderBuilderSimpleIntegrationTests.java @@ -0,0 +1,71 @@ +/* + * Copyright 2012-2018 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.boot.webservices.client; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.boot.testsupport.runner.classpath.ClassPathExclusions; +import org.springframework.boot.testsupport.runner.classpath.ModifiedClassPathRunner; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.ws.transport.WebServiceMessageSender; +import org.springframework.ws.transport.http.ClientHttpRequestMessageSender; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link HttpWebServiceMessageSenderBuilder} when no preferred HTTP clients are + * available + * + * @author Stephane Nicoll + */ +@RunWith(ModifiedClassPathRunner.class) +@ClassPathExclusions({ "httpclient-*.jar", "okhttp*.jar" }) +public class HttpWebServiceMessageSenderBuilderSimpleIntegrationTests { + + private final HttpWebServiceMessageSenderBuilder builder = new HttpWebServiceMessageSenderBuilder(); + + @Test + public void buildUseUseSimpleClientByDefault() { + WebServiceMessageSender messageSender = this.builder.build(); + assertSimpleClientRequestFactory(messageSender); + } + + @Test + public void buildWithCustomTimeouts() { + WebServiceMessageSender messageSender = this.builder.setConnectionTimeout(5000) + .setReadTimeout(2000).build(); + SimpleClientHttpRequestFactory requestFactory = assertSimpleClientRequestFactory( + messageSender); + assertThat(ReflectionTestUtils.getField(requestFactory, "connectTimeout")) + .isEqualTo(5000); + assertThat(ReflectionTestUtils.getField(requestFactory, "readTimeout")) + .isEqualTo(2000); + } + + private SimpleClientHttpRequestFactory assertSimpleClientRequestFactory( + WebServiceMessageSender messageSender) { + assertThat(messageSender).isInstanceOf(ClientHttpRequestMessageSender.class); + ClientHttpRequestMessageSender sender = (ClientHttpRequestMessageSender) messageSender; + ClientHttpRequestFactory requestFactory = sender.getRequestFactory(); + assertThat(requestFactory).isInstanceOf(SimpleClientHttpRequestFactory.class); + return (SimpleClientHttpRequestFactory) requestFactory; + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/HttpWebServiceMessageSenderBuilderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/HttpWebServiceMessageSenderBuilderTests.java new file mode 100644 index 0000000000..e844882a1f --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/HttpWebServiceMessageSenderBuilderTests.java @@ -0,0 +1,84 @@ +/* + * Copyright 2012-2018 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.boot.webservices.client; + +import org.apache.http.client.config.RequestConfig; +import org.junit.Test; + +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.ws.transport.WebServiceMessageSender; +import org.springframework.ws.transport.http.ClientHttpRequestMessageSender; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link HttpWebServiceMessageSenderBuilder}. + * + * @author Stephane Nicoll + */ +public class HttpWebServiceMessageSenderBuilderTests { + + @Test + public void buildWithRequestFactorySupplier() { + ClientHttpRequestFactory requestFactory = mock(ClientHttpRequestFactory.class); + ClientHttpRequestMessageSender messageSender = build( + new HttpWebServiceMessageSenderBuilder() + .requestFactory(() -> requestFactory)); + assertThat(messageSender.getRequestFactory()).isSameAs(requestFactory); + } + + @Test + public void buildWithReadAndConnectTimeout() { + ClientHttpRequestMessageSender messageSender = build( + new HttpWebServiceMessageSenderBuilder() + .requestFactory(SimpleClientHttpRequestFactory::new) + .setConnectionTimeout(5000).setReadTimeout(2000)); + SimpleClientHttpRequestFactory requestFactory = (SimpleClientHttpRequestFactory) messageSender + .getRequestFactory(); + assertThat(ReflectionTestUtils.getField(requestFactory, "connectTimeout")) + .isEqualTo(5000); + assertThat(ReflectionTestUtils.getField(requestFactory, "readTimeout")) + .isEqualTo(2000); + } + + @Test + public void buildUsesHttpComponentsBydefault() { + ClientHttpRequestMessageSender messageSender = build( + new HttpWebServiceMessageSenderBuilder().setConnectionTimeout(5000) + .setReadTimeout(2000)); + ClientHttpRequestFactory requestFactory = messageSender.getRequestFactory(); + assertThat(requestFactory) + .isInstanceOf(HttpComponentsClientHttpRequestFactory.class); + RequestConfig requestConfig = (RequestConfig) ReflectionTestUtils + .getField(requestFactory, "requestConfig"); + assertThat(requestConfig).isNotNull(); + assertThat(requestConfig.getConnectTimeout()).isEqualTo(5000); + assertThat(requestConfig.getSocketTimeout()).isEqualTo(2000); + } + + private ClientHttpRequestMessageSender build( + HttpWebServiceMessageSenderBuilder builder) { + WebServiceMessageSender messageSender = builder.build(); + assertThat(messageSender).isInstanceOf(ClientHttpRequestMessageSender.class); + return ((ClientHttpRequestMessageSender) messageSender); + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderTests.java new file mode 100644 index 0000000000..82480b57b5 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderTests.java @@ -0,0 +1,380 @@ +/* + * Copyright 2012-2018 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.boot.webservices.client; + +import java.net.URI; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; + +import javax.xml.transform.sax.SAXTransformerFactory; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.oxm.Marshaller; +import org.springframework.oxm.Unmarshaller; +import org.springframework.ws.WebServiceMessageFactory; +import org.springframework.ws.client.core.FaultMessageResolver; +import org.springframework.ws.client.core.WebServiceTemplate; +import org.springframework.ws.client.support.destination.DestinationProvider; +import org.springframework.ws.client.support.interceptor.ClientInterceptor; +import org.springframework.ws.transport.WebServiceMessageSender; +import org.springframework.ws.transport.http.ClientHttpRequestMessageSender; +import org.springframework.ws.transport.http.HttpUrlConnectionMessageSender; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Tests for {@link WebServiceTemplateBuilder}. + * + * @author Stephane Nicoll + * @author Dmytro Nosan + */ +public class WebServiceTemplateBuilderTests { + + private final WebServiceTemplateBuilder builder = new WebServiceTemplateBuilder(); + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Mock + private WebServiceMessageSender messageSender; + + @Mock + private ClientInterceptor interceptor; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void createWithCustomizersShouldApplyCustomizers() { + WebServiceTemplateCustomizer customizer = mock( + WebServiceTemplateCustomizer.class); + WebServiceTemplate template = new WebServiceTemplateBuilder(customizer).build(); + verify(customizer).customize(template); + } + + @Test + public void buildShouldDetectHttpMessageSender() { + WebServiceTemplate webServiceTemplate = this.builder.build(); + assertThat(webServiceTemplate.getMessageSenders()).hasSize(1); + WebServiceMessageSender messageSender = webServiceTemplate.getMessageSenders()[0]; + assertHttpComponentsRequestFactory(messageSender); + } + + @Test + public void detectHttpMessageSenderWhenFalseShouldDisableDetection() { + WebServiceTemplate webServiceTemplate = this.builder + .detectHttpMessageSender(false).build(); + assertThat(webServiceTemplate.getMessageSenders()).hasSize(1); + assertThat(webServiceTemplate.getMessageSenders()[0]) + .isInstanceOf(HttpUrlConnectionMessageSender.class); + } + + @Test + public void messageSendersWhenSendersAreAreNullShouldThrowException() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("MessageSenders must not be null"); + this.builder.messageSenders((WebServiceMessageSender[]) null); + } + + @Test + public void messageSendersCollectionWhenSendersAreAreNullShouldThrowException() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("MessageSenders must not be null"); + this.builder.messageSenders((Collection) null); + } + + @Test + public void messageSendersShouldApply() { + WebServiceTemplate template = this.builder.messageSenders(this.messageSender) + .build(); + assertThat(template.getMessageSenders()).containsOnly(this.messageSender); + } + + @Test + public void messageSendersShouldReplaceExisting() { + WebServiceTemplate template = this.builder + .messageSenders(new ClientHttpRequestMessageSender()) + .messageSenders(this.messageSender).build(); + assertThat(template.getMessageSenders()).containsOnly(this.messageSender); + } + + @Test + public void additionalMessageSendersWhenSendersAreAreNullShouldThrowException() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("MessageSenders must not be null"); + this.builder.additionalMessageSenders((WebServiceMessageSender[]) null); + } + + @Test + public void additionalMessageSendersCollectionWhenSendersAreAreNullShouldThrowException() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("MessageSenders must not be null"); + this.builder.additionalMessageSenders( + (Collection) null); + } + + @Test + public void additionalMessageSendersShouldAddToExisting() { + ClientHttpRequestMessageSender httpMessageSender = new ClientHttpRequestMessageSender(); + WebServiceTemplate template = this.builder.messageSenders(httpMessageSender) + .additionalMessageSenders(this.messageSender).build(); + assertThat(template.getMessageSenders()).containsOnly(httpMessageSender, + this.messageSender); + } + + @Test + public void additionalMessageSendersShouldKeepDetectedHttpMessageSender() { + WebServiceTemplate template = this.builder + .additionalMessageSenders(this.messageSender).build(); + assertThat(template.getMessageSenders()).contains(this.messageSender); + assertThat(template.getMessageSenders()).hasSize(2); + } + + @Test + public void interceptorsWhenInterceptorsAreNullShouldThrowException() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Interceptors must not be null"); + this.builder.interceptors((ClientInterceptor[]) null); + } + + @Test + public void interceptorsCollectionWhenInterceptorsAreNullShouldThrowException() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Interceptors must not be null"); + this.builder.interceptors((Collection) null); + } + + @Test + public void interceptorsShouldApply() { + WebServiceTemplate template = this.builder.interceptors(this.interceptor).build(); + assertThat(template.getInterceptors()).containsOnly(this.interceptor); + } + + @Test + public void interceptorsShouldReplaceExisting() { + WebServiceTemplate template = this.builder + .interceptors(mock(ClientInterceptor.class)) + .interceptors(Collections.singleton(this.interceptor)).build(); + assertThat(template.getInterceptors()).containsOnly(this.interceptor); + } + + @Test + public void additionalInterceptorsWhenInterceptorsAreNullShouldThrowException() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Interceptors must not be null"); + this.builder.additionalInterceptors((ClientInterceptor[]) null); + } + + @Test + public void additionalInterceptorsCollectionWhenInterceptorsAreNullShouldThrowException() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Interceptors must not be null"); + this.builder.additionalInterceptors((Set) null); + } + + @Test + public void additionalInterceptorsShouldAddToExisting() { + ClientInterceptor interceptor = mock(ClientInterceptor.class); + WebServiceTemplate template = this.builder.interceptors(interceptor) + .additionalInterceptors(this.interceptor).build(); + assertThat(template.getInterceptors()).containsOnly(interceptor, + this.interceptor); + } + + @Test + public void additionalInterceptorsShouldAddToExistingWebServiceTemplate() { + ClientInterceptor f1 = Mockito.mock(ClientInterceptor.class); + ClientInterceptor f2 = Mockito.mock(ClientInterceptor.class); + WebServiceTemplate webServiceTemplate = new WebServiceTemplate(); + webServiceTemplate.setInterceptors(new ClientInterceptor[] { f1 }); + this.builder.additionalInterceptors(f2).configure(webServiceTemplate); + assertThat(webServiceTemplate.getInterceptors()).containsExactlyInAnyOrder(f2, + f1); + } + + @Test + public void customizersWhenCustomizersAreNullShouldThrowException() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Customizers must not be null"); + this.builder.customizers((WebServiceTemplateCustomizer[]) null); + } + + @Test + public void customizersCollectionWhenCustomizersAreNullShouldThrowException() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Customizers must not be null"); + this.builder + .customizers((Collection) null); + } + + @Test + public void customizersShouldApply() { + WebServiceTemplateCustomizer customizer = mock( + WebServiceTemplateCustomizer.class); + WebServiceTemplate template = this.builder.customizers(customizer).build(); + verify(customizer).customize(template); + } + + @Test + public void customizersShouldBeAppliedLast() { + WebServiceTemplate template = spy(new WebServiceTemplate()); + this.builder + .additionalCustomizers(((webServiceTemplate) -> verify(webServiceTemplate) + .setMessageSenders(any()))); + this.builder.configure(template); + } + + @Test + public void customizersShouldReplaceExisting() { + WebServiceTemplateCustomizer customizer1 = mock( + WebServiceTemplateCustomizer.class); + WebServiceTemplateCustomizer customizer2 = mock( + WebServiceTemplateCustomizer.class); + WebServiceTemplate template = this.builder.customizers(customizer1) + .customizers(Collections.singleton(customizer2)).build(); + verifyZeroInteractions(customizer1); + verify(customizer2).customize(template); + } + + @Test + public void additionalCustomizersWhenCustomizersAreNullShouldThrowException() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Customizers must not be null"); + this.builder.additionalCustomizers((WebServiceTemplateCustomizer[]) null); + } + + @Test + public void additionalCustomizersCollectionWhenCustomizersAreNullShouldThrowException() { + this.thrown.expect(IllegalArgumentException.class); + this.thrown.expectMessage("Customizers must not be null"); + this.builder.additionalCustomizers( + (Collection) null); + } + + @Test + public void additionalCustomizersShouldAddToExisting() { + WebServiceTemplateCustomizer customizer1 = mock( + WebServiceTemplateCustomizer.class); + WebServiceTemplateCustomizer customizer2 = mock( + WebServiceTemplateCustomizer.class); + WebServiceTemplate template = this.builder.customizers(customizer1) + .additionalCustomizers(customizer2).build(); + verify(customizer1).customize(template); + verify(customizer2).customize(template); + } + + @Test + public void setCheckConnectionForFault() { + WebServiceTemplate template = mock(WebServiceTemplate.class); + this.builder.setCheckConnectionForFault(false).configure(template); + verify(template).setCheckConnectionForFault(false); + } + + @Test + public void setCheckConnectionForError() { + WebServiceTemplate template = mock(WebServiceTemplate.class); + this.builder.setCheckConnectionForError(false).configure(template); + verify(template).setCheckConnectionForError(false); + + } + + @Test + public void setTransformerFactoryClass() { + WebServiceTemplate template = mock(WebServiceTemplate.class); + this.builder.setTransformerFactoryClass(SAXTransformerFactory.class) + .configure(template); + verify(template).setTransformerFactoryClass(SAXTransformerFactory.class); + } + + @Test + public void setWebServiceMessageFactory() { + WebServiceMessageFactory messageFactory = mock(WebServiceMessageFactory.class); + WebServiceTemplate template = this.builder + .setWebServiceMessageFactory(messageFactory).build(); + assertThat(template.getMessageFactory()).isEqualTo(messageFactory); + } + + @Test + public void setMarshaller() { + Marshaller marshaller = mock(Marshaller.class); + WebServiceTemplate template = this.builder.setMarshaller(marshaller).build(); + assertThat(template.getMarshaller()).isEqualTo(marshaller); + } + + @Test + public void setUnmarshaller() { + Unmarshaller unmarshaller = mock(Unmarshaller.class); + WebServiceTemplate webServiceTemplate = this.builder.setUnmarshaller(unmarshaller) + .build(); + assertThat(webServiceTemplate.getUnmarshaller()).isEqualTo(unmarshaller); + } + + @Test + public void setFaultMessageResolver() { + FaultMessageResolver faultMessageResolver = mock(FaultMessageResolver.class); + WebServiceTemplate webServiceTemplate = this.builder + .setFaultMessageResolver(faultMessageResolver).build(); + assertThat(webServiceTemplate.getFaultMessageResolver()) + .isEqualTo(faultMessageResolver); + } + + @Test + public void setDefaultUri() { + URI uri = URI.create("http://localhost:8080"); + WebServiceTemplate webServiceTemplate = this.builder.setDefaultUri(uri.toString()) + .build(); + assertThat(webServiceTemplate.getDestinationProvider().getDestination()) + .isEqualTo(uri); + } + + @Test + public void setDestinationProvider() { + DestinationProvider destinationProvider = () -> URI + .create("http://localhost:8080"); + WebServiceTemplate webServiceTemplate = this.builder + .setDestinationProvider(destinationProvider).build(); + assertThat(webServiceTemplate.getDestinationProvider()) + .isEqualTo(destinationProvider); + } + + private void assertHttpComponentsRequestFactory( + WebServiceMessageSender messageSender) { + assertThat(messageSender).isInstanceOf(ClientHttpRequestMessageSender.class); + ClientHttpRequestMessageSender sender = (ClientHttpRequestMessageSender) messageSender; + ClientHttpRequestFactory requestFactory = sender.getRequestFactory(); + assertThat(requestFactory) + .isInstanceOf(HttpComponentsClientHttpRequestFactory.class); + } + +}