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 00000000000..e12ee5b8bcd --- /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 + */ +@Configuration +@ConditionalOnClass({ WebServiceTemplateBuilder.class, 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.setCustomizers(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 00000000000..5677addf450 --- /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 8abd511201e..69b1f0e7904 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 00000000000..d60b88ca655 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/webservices/client/WebServiceTemplateAutoConfigurationTests.java @@ -0,0 +1,150 @@ +/* + * 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 org.junit.After; +import org.junit.Test; + +import org.springframework.boot.webservices.client.WebServiceTemplateBuilder; +import org.springframework.boot.webservices.client.WebServiceTemplateCustomizer; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.oxm.jaxb.Jaxb2Marshaller; +import org.springframework.ws.client.core.WebServiceTemplate; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link WebServiceTemplateAutoConfiguration + * WebServiceTemplateAutoConfiguration}. + * + * @author Dmytro Nosan + */ +public class WebServiceTemplateAutoConfigurationTests { + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void webServiceTemplateShouldNotHaveMarshallerAndUnmarshaller() { + load(WebServiceTemplateConfig.class); + WebServiceTemplate webServiceTemplate = this.context + .getBean(WebServiceTemplate.class); + assertThat(webServiceTemplate.getUnmarshaller()).isNull(); + assertThat(webServiceTemplate.getMarshaller()).isNull(); + } + + @Test + public void webServiceTemplateShouldUserCustomBuilder() { + load(CustomWebServiceTemplateBuilderConfig.class, WebServiceTemplateConfig.class); + WebServiceTemplate webServiceTemplate = this.context + .getBean(WebServiceTemplate.class); + assertThat(webServiceTemplate.getMarshaller()).isNotNull(); + } + + @Test + public void webServiceTemplateShouldApplyCustomizer() { + load(WebServiceTemplateCustomizerConfig.class, WebServiceTemplateConfig.class); + WebServiceTemplate webServiceTemplate = this.context + .getBean(WebServiceTemplate.class); + assertThat(webServiceTemplate.getUnmarshaller()).isNotNull(); + } + + @Test + public void builderShouldBeFreshForEachUse() { + load(DirtyWebServiceTemplateConfig.class); + } + + private void load(Class... config) { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + ctx.register(config); + ctx.register(WebServiceTemplateAutoConfiguration.class); + ctx.refresh(); + this.context = ctx; + } + + @Configuration + static class WebServiceTemplateConfig { + + @Bean + public WebServiceTemplate webServiceTemplate(WebServiceTemplateBuilder builder) { + return builder.build(); + } + + } + + @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.addCustomizers((webServiceTemplate) -> { + throw new IllegalStateException(); + }); + } + + } + + @Configuration + static class CustomWebServiceTemplateBuilderConfig { + + @Bean + public WebServiceTemplateBuilder webServiceTemplateBuilder() { + return new WebServiceTemplateBuilder().setMarshaller(new Jaxb2Marshaller()); + } + + } + + @Configuration + static class WebServiceTemplateCustomizerConfig { + + @Bean + public WebServiceTemplateCustomizer webServiceTemplateCustomizer() { + return (ws) -> ws.setUnmarshaller(new Jaxb2Marshaller()); + } + + } + +} 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 d0677cafd94..beb46654bf8 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 @@ -5634,7 +5634,6 @@ The following code shows a typical example: ---- - [[boot-features-webclient-customization]] === WebClient Customization There are three main approaches to `WebClient` customization, depending on how broadly you @@ -5653,6 +5652,35 @@ the point of injection. Finally, you can fall back to the original API and use `WebClient.create()`. In that case, no auto-configuration or `WebClientCustomizer` is applied. +[[boot-features-webservicetemplate]] +== Calling Web Services with `WebServiceTemplate` +If you need to call remote WEB services from your application, you can use the Spring +Framework's {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 someCall(DetailsReq detailsReq) { + return (DetailsResp) this.webServiceTemplate.marshalSendAndReceive(detailsReq, new SoapActionCallback(ACTION)); + + } + + } +---- [[boot-features-validation]] diff --git a/spring-boot-project/spring-boot/pom.xml b/spring-boot-project/spring-boot/pom.xml index aff395bf3f2..dcbecba6914 100644 --- a/spring-boot-project/spring-boot/pom.xml +++ b/spring-boot-project/spring-boot/pom.xml @@ -245,6 +245,16 @@ spring-orm true + + org.springframework.ws + spring-ws-core + true + + + org.springframework + spring-oxm + true + org.springframework spring-test 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 00000000000..4a185900d58 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilder.java @@ -0,0 +1,1106 @@ +/* + * 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.Field; +import java.net.URI; +import java.time.Duration; +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.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; + +import javax.xml.transform.TransformerFactory; + +import org.springframework.beans.BeanUtils; +import org.springframework.http.client.AbstractClientHttpRequestFactoryWrapper; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.client.OkHttp3ClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.oxm.Marshaller; +import org.springframework.oxm.Unmarshaller; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; +import org.springframework.util.ReflectionUtils; +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.HttpComponentsMessageSender; +import org.springframework.ws.transport.http.HttpUrlConnectionMessageSender; + +/** + * Builder that can be used to configure and create a + * {@link org.springframework.ws.client.core.WebServiceTemplate}. By default the built + * {@link org.springframework.ws.client.core.WebServiceTemplate} will attempt to use the + * most suitable {@link org.springframework.ws.transport.WebServiceMessageSender}, call + * {@link #detectWebServiceMessageSender(boolean) detectWebServiceMessageSender(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 + */ +public class WebServiceTemplateBuilder { + + private static final Map> MESSAGE_SENDER_FACTORY_CLASSES; + + static { + Map> candidates = new LinkedHashMap<>(); + candidates.put("org.apache.http.client.HttpClient", + HttpComponentsMessageSenderFactory.class); + candidates.put("org.springframework.http.client.ClientHttpRequestFactory", + ClientHttpRequestMessageSenderFactory.class); + MESSAGE_SENDER_FACTORY_CLASSES = Collections.unmodifiableMap(candidates); + } + + private final Set interceptors; + + private final Set internalCustomizers; + + private final Set customizers; + + private final Set> webServiceMessageSenderSuppliers; + + private final Set webServiceMessageSenderCustomizers; + + private final Marshaller marshaller; + + private final Unmarshaller unmarshaller; + + private final DestinationProvider destinationProvider; + + private final Class transformerFactoryClass; + + private final WebServiceMessageFactory messageFactory; + + private final boolean detectWebServiceMessageSender; + + public WebServiceTemplateBuilder(WebServiceTemplateCustomizer... customizers) { + this(Collections.emptySet(), Collections.emptySet(), + append(Collections.emptySet(), customizers), + Collections.emptySet(), Collections.emptySet(), null, null, null, null, + null, true); + } + + private WebServiceTemplateBuilder(Set interceptors, + Set internalCustomizers, + Set customizers, + Set> webServiceMessageSenderSuppliers, + Set webServiceMessageSenderCustomizers, + Marshaller marshaller, Unmarshaller unmarshaller, + DestinationProvider destinationProvider, + Class transformerFactoryClass, + WebServiceMessageFactory messageFactory, + boolean detectWebServiceMessageSender) { + this.interceptors = interceptors; + this.internalCustomizers = internalCustomizers; + this.customizers = customizers; + this.webServiceMessageSenderSuppliers = webServiceMessageSenderSuppliers; + this.webServiceMessageSenderCustomizers = webServiceMessageSenderCustomizers; + this.marshaller = marshaller; + this.unmarshaller = unmarshaller; + this.destinationProvider = destinationProvider; + this.transformerFactoryClass = transformerFactoryClass; + this.messageFactory = messageFactory; + this.detectWebServiceMessageSender = detectWebServiceMessageSender; + } + + /** + * Set {@link ClientInterceptor ClientInterceptors} that should be used with the + * {@link WebServiceTemplate}. Interceptors are applied in the order that they were + * added after builder configuration has been applied. + * + * Note! Override existing interceptors + * @param interceptors the interceptors to set + * @return a new builder instance + * @see WebServiceTemplate#setInterceptors(ClientInterceptor[]) + */ + public WebServiceTemplateBuilder setInterceptors(ClientInterceptor... interceptors) { + Assert.notNull(interceptors, "interceptors must not be null"); + return setInterceptors(Arrays.asList(interceptors)); + } + + /** + * Set {@link ClientInterceptor ClientInterceptors} that should be used with the + * {@link WebServiceTemplate}. Interceptors are applied in the order that they were + * added after builder configuration has been applied. + * + * Note! Override existing interceptors + * @param interceptors the interceptors to set + * @return a new builder instance + * @see WebServiceTemplate#setInterceptors(ClientInterceptor[]) + */ + public WebServiceTemplateBuilder setInterceptors( + Collection interceptors) { + Assert.notNull(interceptors, "interceptors must not be null"); + return new WebServiceTemplateBuilder( + append(Collections.emptySet(), interceptors), + this.internalCustomizers, this.customizers, + this.webServiceMessageSenderSuppliers, + this.webServiceMessageSenderCustomizers, this.marshaller, + this.unmarshaller, this.destinationProvider, this.transformerFactoryClass, + this.messageFactory, this.detectWebServiceMessageSender); + } + + /** + * Add additional {@link ClientInterceptor ClientInterceptors} that should be used + * with the {@link WebServiceTemplate}. Interceptors are applied in the order that + * they were added after builder configuration has been applied. + * @param interceptors the interceptors to add + * @return a new builder instance + * @see WebServiceTemplate#setInterceptors(ClientInterceptor[]) + */ + public WebServiceTemplateBuilder addInterceptors(ClientInterceptor... interceptors) { + Assert.notNull(interceptors, "interceptors must not be null"); + return addInterceptors(Arrays.asList(interceptors)); + } + + /** + * Add additional {@link ClientInterceptor ClientInterceptors} that should be used + * with the {@link WebServiceTemplate}. Interceptors are applied in the order that + * they were added after builder configuration has been applied. + * @param interceptors the interceptors to add + * @return a new builder instance + * @see WebServiceTemplate#setInterceptors(ClientInterceptor[]) + */ + public WebServiceTemplateBuilder addInterceptors( + Collection interceptors) { + Assert.notNull(interceptors, "interceptors must not be null"); + return new WebServiceTemplateBuilder(append(this.interceptors, interceptors), + this.internalCustomizers, this.customizers, + this.webServiceMessageSenderSuppliers, + this.webServiceMessageSenderCustomizers, this.marshaller, + this.unmarshaller, this.destinationProvider, this.transformerFactoryClass, + this.messageFactory, this.detectWebServiceMessageSender); + } + + /** + * 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. + * + * Note! Override existing customizers + * @param customizers the customizers to set + * @return a new builder instance + */ + + public WebServiceTemplateBuilder setCustomizers( + Collection customizers) { + Assert.notNull(customizers, "customizers must not be null"); + return new WebServiceTemplateBuilder(this.interceptors, this.internalCustomizers, + append(Collections.emptySet(), customizers), + this.webServiceMessageSenderSuppliers, + this.webServiceMessageSenderCustomizers, this.marshaller, + this.unmarshaller, this.destinationProvider, this.transformerFactoryClass, + this.messageFactory, this.detectWebServiceMessageSender); + } + + /** + * 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. + * + * Note! Override existing customizers + * @param customizers the customizers to set + * @return a new builder instance + */ + public WebServiceTemplateBuilder setCustomizers( + WebServiceTemplateCustomizer... customizers) { + Assert.notNull(customizers, "customizers must not be null"); + return setCustomizers(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 + */ + public WebServiceTemplateBuilder addCustomizers( + WebServiceTemplateCustomizer... customizers) { + Assert.notNull(customizers, "customizers must not be null"); + return addCustomizers(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 + */ + + public WebServiceTemplateBuilder addCustomizers( + Collection customizers) { + Assert.notNull(customizers, "customizers must not be null"); + return new WebServiceTemplateBuilder(this.interceptors, this.internalCustomizers, + append(this.customizers, customizers), + this.webServiceMessageSenderSuppliers, + this.webServiceMessageSenderCustomizers, this.marshaller, + this.unmarshaller, this.destinationProvider, this.transformerFactoryClass, + this.messageFactory, this.detectWebServiceMessageSender); + } + + /** + * Sets the {@code Suppliers} of {@link WebServiceMessageSender} that should be called + * each time when {@link #configure(WebServiceTemplate)} method is called. + * + * Note! Override existing WebServiceMessageSender {@code suppliers} + * @param webServiceMessageSenderSuppliers Suppliers for the messageSenders + * @return a new builder instance. + * @see WebServiceTemplate#setMessageSenders(WebServiceMessageSender[]) + */ + + public WebServiceTemplateBuilder setWebServiceMessageSenders( + Collection> webServiceMessageSenderSuppliers) { + Assert.notNull(webServiceMessageSenderSuppliers, + "webServiceMessageSenderSuppliers must not be null"); + return new WebServiceTemplateBuilder(this.interceptors, this.internalCustomizers, + this.customizers, + append(Collections + .>emptySet(), + webServiceMessageSenderSuppliers), + this.webServiceMessageSenderCustomizers, this.marshaller, + this.unmarshaller, this.destinationProvider, this.transformerFactoryClass, + this.messageFactory, this.detectWebServiceMessageSender); + } + + /** + * Add additional {@code Suppliers} of {@link WebServiceMessageSender} that should be + * called each time when {@link #configure(WebServiceTemplate)} method is called. + * @param webServiceMessageSenderSuppliers Suppliers for the messageSenders + * @return a new builder instance. + * @see WebServiceTemplate#setMessageSenders(WebServiceMessageSender[]) + */ + public WebServiceTemplateBuilder addWebServiceMessageSenders( + Collection> webServiceMessageSenderSuppliers) { + Assert.notNull(webServiceMessageSenderSuppliers, + "webServiceMessageSenderSuppliers must not be null"); + return new WebServiceTemplateBuilder(this.interceptors, this.internalCustomizers, + this.customizers, + append(this.webServiceMessageSenderSuppliers, + webServiceMessageSenderSuppliers), + this.webServiceMessageSenderCustomizers, this.marshaller, + this.unmarshaller, this.destinationProvider, this.transformerFactoryClass, + this.messageFactory, this.detectWebServiceMessageSender); + } + + /** + * Set {@link WebServiceTemplate#setCheckConnectionForFault(boolean) + * setCheckConnectionForFault} on the underlying. + * @param checkConnectionForFault Specify whether checkConnectionForFault should be + * enabled or not. + * @return a new builder instance. + **/ + public WebServiceTemplateBuilder setCheckConnectionForFault( + boolean checkConnectionForFault) { + return new WebServiceTemplateBuilder(this.interceptors, + append(this.internalCustomizers, + new CheckConnectionFaultCustomizer(checkConnectionForFault)), + this.customizers, this.webServiceMessageSenderSuppliers, + this.webServiceMessageSenderCustomizers, this.marshaller, + this.unmarshaller, this.destinationProvider, this.transformerFactoryClass, + this.messageFactory, this.detectWebServiceMessageSender); + } + + /** + * Set {@link WebServiceTemplate#setCheckConnectionForError(boolean) + * setCheckConnectionForError} on the underlying. + * @param checkConnectionForError Specify whether checkConnectionForError should be + * enabled or not. + * @return a new builder instance. + **/ + + public WebServiceTemplateBuilder setCheckConnectionForError( + boolean checkConnectionForError) { + return new WebServiceTemplateBuilder(this.interceptors, + append(this.internalCustomizers, + new CheckConnectionForErrorCustomizer(checkConnectionForError)), + this.customizers, this.webServiceMessageSenderSuppliers, + this.webServiceMessageSenderCustomizers, this.marshaller, + this.unmarshaller, this.destinationProvider, this.transformerFactoryClass, + this.messageFactory, this.detectWebServiceMessageSender); + } + + /** + * Sets the {@code Supplier} of {@link WebServiceMessageSender} that should be called + * each time when {@link #configure(WebServiceTemplate)} method is called. + * + * Note! Override existing WebServiceMessageSender {@code suppliers} + * @param webServiceMessageSenderSupplier Supplier for the messageSender + * @return a new builder instance. + * @see WebServiceTemplate#setMessageSenders(WebServiceMessageSender[]) + * @see #setWebServiceMessageSenders(Collection) + */ + + public WebServiceTemplateBuilder setWebServiceMessageSender( + Supplier webServiceMessageSenderSupplier) { + Assert.notNull(webServiceMessageSenderSupplier, + "webServiceMessageSenderSupplier must not be null"); + return setWebServiceMessageSenders( + Collections.singleton(webServiceMessageSenderSupplier)); + } + + /** + * Add additional {@code Supplier} of {@link WebServiceMessageSender} that should be + * called each time when {@link #configure(WebServiceTemplate)} method is called. + * @param webServiceMessageSenderSupplier Supplier for the messageSender + * @return a new builder instance. + * @see WebServiceTemplate#setMessageSenders(WebServiceMessageSender[]) + * @see #addWebServiceMessageSenders(Collection) + */ + public WebServiceTemplateBuilder addWebServiceMessageSender( + Supplier webServiceMessageSenderSupplier) { + Assert.notNull(webServiceMessageSenderSupplier, + "webServiceMessageSenderSupplier must not be null"); + return addWebServiceMessageSenders( + Collections.singleton(webServiceMessageSenderSupplier)); + } + + /** + * Sets the {@code Class} of {@link WebServiceMessageSender} that should be created + * each time when {@link #configure(WebServiceTemplate)} method is called. + * + * Note! Override existing WebServiceMessageSender {@code suppliers} + * @param webServiceMessageSenderClass {@code Class} of + * {@link WebServiceMessageSender} + * @return a new builder instance. + * @see WebServiceTemplate#setMessageSenders(WebServiceMessageSender[]) + * @see #setWebServiceMessageSender(Supplier) + * @see BeanUtils#instantiateClass(Class) + */ + + public WebServiceTemplateBuilder setWebServiceMessageSender( + Class webServiceMessageSenderClass) { + Assert.notNull(webServiceMessageSenderClass, + "webServiceMessageSenderClass must not be null"); + return setWebServiceMessageSender( + supplier(webServiceMessageSenderClass, BeanUtils::instantiateClass)); + } + + /** + * Add additional {@code Class} of {@link WebServiceMessageSender} that should be + * created each time when {@link #configure(WebServiceTemplate)} method is called. + * @param webServiceMessageSenderClass {@code Class} of + * {@link WebServiceMessageSender} + * @return a new builder instance. + * @see WebServiceTemplate#setMessageSenders(WebServiceMessageSender[]) + * @see #addWebServiceMessageSender(Supplier) + * @see BeanUtils#instantiateClass(Class) + */ + public WebServiceTemplateBuilder addWebServiceMessageSender( + Class webServiceMessageSenderClass) { + Assert.notNull(webServiceMessageSenderClass, + "webServiceMessageSenderClass must not be null"); + return addWebServiceMessageSender( + supplier(webServiceMessageSenderClass, BeanUtils::instantiateClass)); + } + + /** + * Sets the message factory used for creating messages. + * @param messageFactory instance of WebServiceMessageFactory + * @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.interceptors, this.internalCustomizers, + this.customizers, this.webServiceMessageSenderSuppliers, + this.webServiceMessageSenderCustomizers, this.marshaller, + this.unmarshaller, this.destinationProvider, this.transformerFactoryClass, + messageFactory, this.detectWebServiceMessageSender); + } + + /** + * Set {@link WebServiceTemplate#setUnmarshaller(Unmarshaller) unmarshaller} on the + * underlying. + * @param unmarshaller message unmarshaller + * @return a new builder instance. + * @see WebServiceTemplate#setUnmarshaller(Unmarshaller) + **/ + public WebServiceTemplateBuilder setUnmarshaller(Unmarshaller unmarshaller) { + Assert.notNull(unmarshaller, "unmarshaller must not be null"); + return new WebServiceTemplateBuilder(this.interceptors, this.internalCustomizers, + this.customizers, this.webServiceMessageSenderSuppliers, + this.webServiceMessageSenderCustomizers, this.marshaller, unmarshaller, + this.destinationProvider, this.transformerFactoryClass, + this.messageFactory, this.detectWebServiceMessageSender); + } + + /** + * Set {@link WebServiceTemplate#setMarshaller(Marshaller) marshaller} on the + * underlying. + * @param marshaller message marshaller + * @return a new builder instance. + * @see WebServiceTemplate#setMarshaller(Marshaller) + **/ + public WebServiceTemplateBuilder setMarshaller(Marshaller marshaller) { + Assert.notNull(marshaller, "marshaller must not be null"); + return new WebServiceTemplateBuilder(this.interceptors, this.internalCustomizers, + this.customizers, this.webServiceMessageSenderSuppliers, + this.webServiceMessageSenderCustomizers, marshaller, this.unmarshaller, + this.destinationProvider, this.transformerFactoryClass, + this.messageFactory, this.detectWebServiceMessageSender); + } + + /** + * Sets the connection timeout in milliseconds on the underlying. + * @param connectionTimeout the connection timeout in milliseconds + * @return a new builder instance. + * @throws java.lang.IllegalStateException if the underlying source doesn't support a + * connection timeout. + */ + public WebServiceTemplateBuilder setConnectionTimeout(int connectionTimeout) { + return new WebServiceTemplateBuilder(this.interceptors, this.internalCustomizers, + this.customizers, this.webServiceMessageSenderSuppliers, + append(this.webServiceMessageSenderCustomizers, + new ConnectionTimeoutWebServiceMessageSenderCustomizer( + connectionTimeout)), + this.marshaller, this.unmarshaller, this.destinationProvider, + this.transformerFactoryClass, this.messageFactory, + this.detectWebServiceMessageSender); + } + + /** + * Sets the read timeout in milliseconds on the underlying. + * @param readTimeout the read timeout in milliseconds + * @return a new builder instance. + * @throws java.lang.IllegalStateException if the underlying source doesn't support a + * read timeout. + */ + public WebServiceTemplateBuilder setReadTimeout(int readTimeout) { + return new WebServiceTemplateBuilder(this.interceptors, this.internalCustomizers, + this.customizers, this.webServiceMessageSenderSuppliers, + append(this.webServiceMessageSenderCustomizers, + new ReadTimeoutWebServiceMessageSenderCustomizer(readTimeout)), + this.marshaller, this.unmarshaller, this.destinationProvider, + this.transformerFactoryClass, this.messageFactory, + this.detectWebServiceMessageSender); + } + + /** + * Set {@link WebServiceTemplate#setFaultMessageResolver(FaultMessageResolver) + * faultMessageResolver} on the underlying. + * @param faultMessageResolver faultMessageResolver may be set to null to disable + * fault handling. + * @return a new builder instance. + **/ + public WebServiceTemplateBuilder setFaultMessageResolver( + FaultMessageResolver faultMessageResolver) { + return new WebServiceTemplateBuilder(this.interceptors, + append(this.internalCustomizers, + new FaultMessageResolverCustomizer(faultMessageResolver)), + this.customizers, this.webServiceMessageSenderSuppliers, + this.webServiceMessageSenderCustomizers, this.marshaller, + this.unmarshaller, this.destinationProvider, this.transformerFactoryClass, + this.messageFactory, this.detectWebServiceMessageSender); + } + + /** + * Set {@link WebServiceTemplate#setTransformerFactoryClass(Class) + * setTransformerFactoryClass} on the underlying. + * @param transformerFactoryClass boolean value + * @return a new builder instance. + **/ + + public WebServiceTemplateBuilder setTransformerFactoryClass( + Class transformerFactoryClass) { + Assert.notNull(transformerFactoryClass, + "transformerFactoryClass must not be null"); + return new WebServiceTemplateBuilder(this.interceptors, this.internalCustomizers, + this.customizers, this.webServiceMessageSenderSuppliers, + this.webServiceMessageSenderCustomizers, this.marshaller, + this.unmarshaller, this.destinationProvider, transformerFactoryClass, + this.messageFactory, this.detectWebServiceMessageSender); + } + + /** + * Set the default URI to be used on operations that do not have a URI parameter. + * + * Note!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. + */ + public WebServiceTemplateBuilder setDefaultUri(String defaultUri) { + Assert.hasText(defaultUri, "defaultUri must not be empty"); + return setDestinationProvider(() -> URI.create(defaultUri)); + } + + /** + * Set {@link WebServiceTemplate#setDestinationProvider(DestinationProvider) + * destinationProvider} on the underlying. + * + * Note!Typically, either this property is set, or + * {@link #setDefaultUri(String)}, but not both. + * @param destinationProvider the destination provider URI to be used on operations + * that do not have a URI parameter. + * @return a new builder instance. + */ + public WebServiceTemplateBuilder setDestinationProvider( + DestinationProvider destinationProvider) { + Assert.notNull(destinationProvider, "destinationProvider must not be null"); + return new WebServiceTemplateBuilder(this.interceptors, this.internalCustomizers, + this.customizers, this.webServiceMessageSenderSuppliers, + this.webServiceMessageSenderCustomizers, this.marshaller, + this.unmarshaller, destinationProvider, this.transformerFactoryClass, + this.messageFactory, this.detectWebServiceMessageSender); + } + + /** + * Set if the {@link WebServiceMessageSender} should be detected based on the + * classpath. Default is {@code true}. + * @param detectWebServiceMessageSender if the {@link WebServiceMessageSender} should + * be detected + * @return a new builder instance + */ + public WebServiceTemplateBuilder detectWebServiceMessageSender( + boolean detectWebServiceMessageSender) { + return new WebServiceTemplateBuilder(this.interceptors, this.internalCustomizers, + this.customizers, this.webServiceMessageSenderSuppliers, + this.webServiceMessageSenderCustomizers, this.marshaller, + this.unmarshaller, this.destinationProvider, this.transformerFactoryClass, + this.messageFactory, detectWebServiceMessageSender); + } + + /** + * 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"); + + configureSenders(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.customizers)) { + for (WebServiceTemplateCustomizer customizer : this.customizers) { + customizer.customize(webServiceTemplate); + } + } + + if (!CollectionUtils.isEmpty(this.interceptors)) { + webServiceTemplate.setInterceptors( + append(this.interceptors, webServiceTemplate.getInterceptors()) + .toArray(new ClientInterceptor[0])); + } + + return webServiceTemplate; + } + + private void configureSenders(T webServiceTemplate) { + + if (!CollectionUtils.isEmpty(this.webServiceMessageSenderSuppliers)) { + Set webServiceMessageSenders = new LinkedHashSet<>(); + for (Supplier webServiceMessageSenderSupplier : this.webServiceMessageSenderSuppliers) { + webServiceMessageSenders.add(webServiceMessageSenderSupplier.get()); + } + webServiceTemplate.setMessageSenders( + webServiceMessageSenders.toArray(new WebServiceMessageSender[0])); + } + else if (this.detectWebServiceMessageSender) { + webServiceTemplate.setMessageSenders( + new WebServiceMessageSender[] { detectMessageSender() }); + } + + if (!CollectionUtils.isEmpty(this.webServiceMessageSenderCustomizers)) { + if (!ObjectUtils.isEmpty(webServiceTemplate.getMessageSenders())) { + for (WebServiceMessageSender webServiceMessageSender : webServiceTemplate + .getMessageSenders()) { + for (WebServiceMessageSenderCustomizer webServiceMessageSenderCustomizer : this.webServiceMessageSenderCustomizers) { + webServiceMessageSenderCustomizer + .customize(webServiceMessageSender); + } + } + } + } + } + + private WebServiceMessageSender detectMessageSender() { + ClassLoader classLoader = getClass().getClassLoader(); + for (Map.Entry> candidate : MESSAGE_SENDER_FACTORY_CLASSES + .entrySet()) { + if (ClassUtils.isPresent(candidate.getKey(), classLoader)) { + WebServiceMessageSenderFactory webServiceMessageSenderFactory = BeanUtils + .instantiateClass(candidate.getValue()); + Optional webServiceMessageSender = webServiceMessageSenderFactory + .create(); + if (webServiceMessageSender.isPresent()) { + return webServiceMessageSender.get(); + } + } + } + return new HttpUrlConnectionMessageSender(); + } + + private static Supplier supplier(T value, Function mapper) { + return () -> mapper.apply(value); + } + + 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); + } + + private interface WebServiceMessageSenderFactory { + + Optional create(); + + } + + private interface WebServiceMessageSenderCustomizer { + + void customize(WebServiceMessageSender webServiceMessageSender); + + } + + private static final class ClientHttpRequestMessageSenderFactory + implements WebServiceMessageSenderFactory { + + private static final Map REQUEST_FACTORY_CANDIDATES; + + static { + Map candidates = new LinkedHashMap<>(); + candidates.put("okhttp3.OkHttpClient", + "org.springframework.http.client.OkHttp3ClientHttpRequestFactory"); + REQUEST_FACTORY_CANDIDATES = Collections.unmodifiableMap(candidates); + } + + @Override + public Optional create() { + ClassLoader classLoader = getClass().getClassLoader(); + for (Map.Entry candidate : REQUEST_FACTORY_CANDIDATES + .entrySet()) { + if (ClassUtils.isPresent(candidate.getKey(), classLoader)) { + Class factoryClass = ClassUtils + .resolveClassName(candidate.getValue(), classLoader); + ClientHttpRequestFactory clientHttpRequestFactory = (ClientHttpRequestFactory) BeanUtils + .instantiateClass(factoryClass); + return Optional.of( + new ClientHttpRequestMessageSender(clientHttpRequestFactory)); + } + } + return Optional.empty(); + } + + } + + private static final class HttpComponentsMessageSenderFactory + implements WebServiceMessageSenderFactory { + + @Override + public Optional create() { + return Optional.of(new HttpComponentsMessageSender()); + } + + } + + /** + * {@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); + } + + } + + /** + * {@link WebServiceMessageSenderCustomizer} to set connection timeout. + */ + private static final class ConnectionTimeoutWebServiceMessageSenderCustomizer + extends TimeoutWebServiceMessageSenderCustomizer { + + private ConnectionTimeoutWebServiceMessageSenderCustomizer(int connectTimeout) { + super(connectTimeout, Timeout.CONNECTION); + } + + } + + /** + * {@link WebServiceMessageSenderCustomizer} to set read timeout. + */ + private static final class ReadTimeoutWebServiceMessageSenderCustomizer + extends TimeoutWebServiceMessageSenderCustomizer { + + private ReadTimeoutWebServiceMessageSenderCustomizer(int readTimeout) { + super(readTimeout, Timeout.READ); + } + + } + + private abstract static class TimeoutWebServiceMessageSenderCustomizer + implements WebServiceMessageSenderCustomizer { + + private static final Map>> CUSTOMIZERS; + + static { + Map>> candidates = new LinkedHashMap<>(); + candidates.put( + "org.springframework.ws.transport.http.HttpComponentsMessageSender", + HttpComponentsTimeoutCustomizer.class); + candidates.put( + "org.springframework.ws.transport.http.ClientHttpRequestMessageSender", + ClientHttpRequestTimeoutCustomizer.class); + candidates.put( + "org.springframework.ws.transport.http.HttpUrlConnectionMessageSender", + HttpUrlConnectionTimeoutCustomizer.class); + CUSTOMIZERS = Collections.unmodifiableMap(candidates); + } + + private final Timeout type; + + private final int timeout; + + TimeoutWebServiceMessageSenderCustomizer(int timeout, Timeout type) { + this.timeout = timeout; + this.type = type; + } + + @Override + public final void customize(WebServiceMessageSender webServiceMessageSender) { + ClassLoader classLoader = getClass().getClassLoader(); + customize(CUSTOMIZERS, webServiceMessageSender, this.type, this.timeout, + classLoader); + + } + + @SuppressWarnings("unchecked") + private static void customize( + Map>> customizers, + T target, Timeout type, int timeout, ClassLoader classLoader) { + for (Map.Entry>> candidate : customizers + .entrySet()) { + if (ClassUtils.isPresent(candidate.getKey(), classLoader)) { + Class candidateClass = ClassUtils + .resolveClassName(candidate.getKey(), classLoader); + if (ClassUtils.isAssignable(candidateClass, target.getClass())) { + TimeoutCustomizer timeoutCustomizer = BeanUtils + .instantiateClass(candidate.getValue()); + customize(timeoutCustomizer, target, type, timeout); + return; + } + } + } + throw new IllegalStateException("There is no way to customize '" + + target.getClass() + "' " + "with '" + type.name().toLowerCase() + + "Timeout'. Please use a custom " + "customizer."); + + } + + private static void customize(TimeoutCustomizer customizer, T target, + Timeout type, int timeout) { + if (type == Timeout.CONNECTION) { + customizer.setConnectionTimeout(target, timeout); + } + else if (type == Timeout.READ) { + customizer.setReadTimeout(target, timeout); + } + } + + interface TimeoutCustomizer { + + void setReadTimeout(T source, int timeout); + + void setConnectionTimeout(T source, int timeout); + + } + + enum Timeout { + + READ, CONNECTION + + } + + private static final class HttpComponentsTimeoutCustomizer + implements TimeoutCustomizer { + + @Override + public void setReadTimeout(HttpComponentsMessageSender source, int timeout) { + source.setReadTimeout(timeout); + } + + @Override + public void setConnectionTimeout(HttpComponentsMessageSender source, + int timeout) { + source.setConnectionTimeout(timeout); + } + + } + + private static final class HttpUrlConnectionTimeoutCustomizer + implements TimeoutCustomizer { + + @Override + public void setReadTimeout(HttpUrlConnectionMessageSender source, + int timeout) { + source.setReadTimeout(Duration.ofMillis(timeout)); + } + + @Override + public void setConnectionTimeout(HttpUrlConnectionMessageSender source, + int timeout) { + source.setConnectionTimeout(Duration.ofMillis(timeout)); + } + + } + + private static final class ClientHttpRequestTimeoutCustomizer + implements TimeoutCustomizer { + + private static final Map>> CUSTOMIZERS; + + static { + Map>> candidates = new LinkedHashMap<>(); + candidates.put( + "org.springframework.http.client.HttpComponentsClientHttpRequestFactory", + HttpComponentsClientHttpRequestFactoryTimeoutCustomizer.class); + candidates.put( + "org.springframework.http.client.OkHttp3ClientHttpRequestFactory", + OkHttp3ClientHttpRequestFactoryTimeoutCustomizer.class); + candidates.put( + "org.springframework.http.client.SimpleClientHttpRequestFactory", + SimpleClientHttpRequestFactoryTimeoutCustomizer.class); + CUSTOMIZERS = Collections.unmodifiableMap(candidates); + } + + @Override + public void setReadTimeout(ClientHttpRequestMessageSender source, + int timeout) { + ClassLoader classLoader = getClass().getClassLoader(); + customize(CUSTOMIZERS, getRequestFactory(source), Timeout.READ, timeout, + classLoader); + } + + @Override + public void setConnectionTimeout(ClientHttpRequestMessageSender source, + int timeout) { + ClassLoader classLoader = getClass().getClassLoader(); + customize(CUSTOMIZERS, getRequestFactory(source), Timeout.CONNECTION, + timeout, classLoader); + } + + private ClientHttpRequestFactory getRequestFactory( + ClientHttpRequestMessageSender source) { + ClientHttpRequestFactory requestFactory = source.getRequestFactory(); + if (!(requestFactory instanceof AbstractClientHttpRequestFactoryWrapper)) { + return requestFactory; + } + Field field = ReflectionUtils.findField( + AbstractClientHttpRequestFactoryWrapper.class, "requestFactory"); + Assert.notNull(field, "Field must not be null"); + ReflectionUtils.makeAccessible(field); + do { + requestFactory = (ClientHttpRequestFactory) ReflectionUtils + .getField(field, requestFactory); + } + while (requestFactory instanceof AbstractClientHttpRequestFactoryWrapper); + return requestFactory; + } + + private static final class SimpleClientHttpRequestFactoryTimeoutCustomizer + implements TimeoutCustomizer { + + @Override + public void setReadTimeout(SimpleClientHttpRequestFactory source, + int timeout) { + source.setReadTimeout(timeout); + } + + @Override + public void setConnectionTimeout(SimpleClientHttpRequestFactory source, + int timeout) { + source.setConnectTimeout(timeout); + } + + } + + private static final class HttpComponentsClientHttpRequestFactoryTimeoutCustomizer + implements TimeoutCustomizer { + + @Override + public void setReadTimeout(HttpComponentsClientHttpRequestFactory source, + int timeout) { + source.setReadTimeout(timeout); + } + + @Override + public void setConnectionTimeout( + HttpComponentsClientHttpRequestFactory source, int timeout) { + source.setConnectTimeout(timeout); + } + + } + + private static final class OkHttp3ClientHttpRequestFactoryTimeoutCustomizer + implements TimeoutCustomizer { + + @Override + public void setReadTimeout(OkHttp3ClientHttpRequestFactory source, + int timeout) { + source.setReadTimeout(timeout); + } + + @Override + public void setConnectionTimeout(OkHttp3ClientHttpRequestFactory source, + int timeout) { + source.setConnectTimeout(timeout); + } + + } + + } + + } + +} 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 00000000000..f619e6f9996 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/webservices/client/WebServiceTemplateCustomizer.java @@ -0,0 +1,34 @@ +/* + * 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 + */ +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 00000000000..5e0d4ce13fe --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/webservices/client/package-info.java @@ -0,0 +1,21 @@ +/* + * 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/WebServiceTemplateBuilderCustomsMessageSenderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderCustomsMessageSenderTests.java new file mode 100644 index 00000000000..8ae3040b951 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderCustomsMessageSenderTests.java @@ -0,0 +1,93 @@ +/* + * 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.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Mockito; + +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.ws.transport.WebServiceMessageSender; +import org.springframework.ws.transport.http.ClientHttpRequestMessageSender; + +/** + * Tests for + * {@link org.springframework.boot.webservices.client.WebServiceTemplateBuilder}. + * + * @author Dmytro Nosan + */ +public class WebServiceTemplateBuilderCustomsMessageSenderTests { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private WebServiceTemplateBuilder builder = new WebServiceTemplateBuilder(); + + @Test + public void unknownSenderReadTimeout() { + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage("with 'readTimeout'. Please use a custom customizer."); + this.thrown.expectMessage("There is no way to customize"); + + this.builder.setReadTimeout(3000).setWebServiceMessageSender( + () -> Mockito.mock(WebServiceMessageSender.class)).build(); + } + + @Test + public void unknownSenderConnectionTimeout() { + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage( + "with 'connectionTimeout'. Please use a custom customizer."); + this.thrown.expectMessage("There is no way to customize"); + + this.builder.setConnectionTimeout(3000).setWebServiceMessageSender( + () -> Mockito.mock(WebServiceMessageSender.class)).build(); + } + + @Test + public void unknownRequestFactoryReadTimeout() { + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage("with 'readTimeout'. Please use a custom customizer."); + this.thrown.expectMessage("There is no way to customize"); + + this.builder.setReadTimeout(3000) + .setWebServiceMessageSender(() -> new ClientHttpRequestMessageSender( + Mockito.mock(ClientHttpRequestFactory.class))) + .build(); + } + + @Test + public void unknownRequestFactoryConnectionTimeout() { + this.thrown.expect(IllegalStateException.class); + this.thrown.expectMessage( + "with 'connectionTimeout'. Please use a custom customizer."); + this.thrown.expectMessage("There is no way to customize"); + + this.builder.setConnectionTimeout(3000) + .setWebServiceMessageSender(() -> new ClientHttpRequestMessageSender( + Mockito.mock(ClientHttpRequestFactory.class))) + .build(); + } + + @Test + public void shouldBuildWithoutTimeouts() { + this.builder.setWebServiceMessageSender( + () -> Mockito.mock(WebServiceMessageSender.class)).build(); + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderHttpComponentsClientHttpRequestFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderHttpComponentsClientHttpRequestFactoryTests.java new file mode 100644 index 00000000000..f2cafa15c7c --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderHttpComponentsClientHttpRequestFactoryTests.java @@ -0,0 +1,55 @@ +/* + * 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.BufferingClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.ws.transport.http.ClientHttpRequestMessageSender; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link WebServiceTemplateBuilder}. + * + * @author Dmytro Nosan + */ +public class WebServiceTemplateBuilderHttpComponentsClientHttpRequestFactoryTests { + + private WebServiceTemplateBuilder builder = new WebServiceTemplateBuilder(); + + @Test + public void setTimeout() { + HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); + ClientHttpRequestMessageSender sender = new ClientHttpRequestMessageSender( + new BufferingClientHttpRequestFactory(factory)); + + this.builder.setConnectionTimeout(5000).setReadTimeout(2000) + .setWebServiceMessageSender(() -> sender).build(); + + RequestConfig requestConfig = (RequestConfig) ReflectionTestUtils + .getField(factory, "requestConfig"); + assertThat(requestConfig).isNotNull(); + assertThat(requestConfig.getConnectTimeout()).isEqualTo(5000); + assertThat(requestConfig.getSocketTimeout()).isEqualTo(2000); + + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderHttpComponentsMessageSenderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderHttpComponentsMessageSenderTests.java new file mode 100644 index 00000000000..82f08a27de3 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderHttpComponentsMessageSenderTests.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.webservices.client; + +import org.apache.http.client.HttpClient; +import org.apache.http.params.HttpConnectionParams; +import org.junit.Test; + +import org.springframework.ws.client.core.WebServiceTemplate; +import org.springframework.ws.transport.http.HttpComponentsMessageSender; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for + * {@link org.springframework.boot.webservices.client.WebServiceTemplateBuilder}. This + * test class check that builder will create HttpComponents by default if apache client is + * present in the classpath. + * + * @author Dmytro Nosan + */ +@SuppressWarnings("deprecation") +public class WebServiceTemplateBuilderHttpComponentsMessageSenderTests { + + private WebServiceTemplateBuilder builder = new WebServiceTemplateBuilder(); + + @Test + public void build() { + WebServiceTemplate webServiceTemplate = new WebServiceTemplateBuilder().build(); + + assertThat(webServiceTemplate.getMessageSenders()).hasSize(1); + + assertThat(webServiceTemplate.getMessageSenders()[0]) + .isInstanceOf(HttpComponentsMessageSender.class); + + } + + @Test + public void setTimeout() { + HttpComponentsMessageSender sender = new HttpComponentsMessageSender(); + HttpClient httpClient = sender.getHttpClient(); + + this.builder.setConnectionTimeout(5000).setReadTimeout(2000) + .setWebServiceMessageSender(() -> sender).build(); + + assertThat(HttpConnectionParams.getConnectionTimeout(httpClient.getParams())) + .isEqualTo(5000); + assertThat(HttpConnectionParams.getSoTimeout(httpClient.getParams())) + .isEqualTo(2000); + + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderHttpUrlConnectionMessageSenderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderHttpUrlConnectionMessageSenderTests.java new file mode 100644 index 00000000000..3468091f6ee --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderHttpUrlConnectionMessageSenderTests.java @@ -0,0 +1,70 @@ +/* + * 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.time.Duration; + +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.test.util.ReflectionTestUtils; +import org.springframework.ws.client.core.WebServiceTemplate; +import org.springframework.ws.transport.http.HttpUrlConnectionMessageSender; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link WebServiceTemplateBuilder}. This test class check that builder will + * create HttpUrlConnectionMessageSender If Ok-http and Apache client are not present in + * the classpath. + * + * @author Dmytro Nosan + */ +@RunWith(ModifiedClassPathRunner.class) +@ClassPathExclusions({ "httpclient-*.jar", "okhttp-*.jar" }) +public class WebServiceTemplateBuilderHttpUrlConnectionMessageSenderTests { + + private WebServiceTemplateBuilder builder = new WebServiceTemplateBuilder(); + + @Test + public void build() { + WebServiceTemplate webServiceTemplate = this.builder.build(); + + assertThat(webServiceTemplate.getMessageSenders()).hasSize(1); + + assertThat(webServiceTemplate.getMessageSenders()[0]) + .isInstanceOf(HttpUrlConnectionMessageSender.class); + + } + + @Test + public void setTimeout() { + HttpUrlConnectionMessageSender sender = new HttpUrlConnectionMessageSender(); + + this.builder.setConnectionTimeout(5000).setReadTimeout(2000) + .setWebServiceMessageSender(() -> sender).build(); + + assertThat(ReflectionTestUtils.getField(sender, "connectionTimeout")) + .isEqualTo(Duration.ofMillis(5000)); + assertThat(ReflectionTestUtils.getField(sender, "readTimeout")) + .isEqualTo(Duration.ofMillis(2000)); + + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderOkHttp3ClientHttpRequestFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderOkHttp3ClientHttpRequestFactoryTests.java new file mode 100644 index 00000000000..3d37a488f11 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderOkHttp3ClientHttpRequestFactoryTests.java @@ -0,0 +1,82 @@ +/* + * 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.BufferingClientHttpRequestFactory; +import org.springframework.http.client.OkHttp3ClientHttpRequestFactory; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.ws.client.core.WebServiceTemplate; +import org.springframework.ws.transport.http.ClientHttpRequestMessageSender; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link WebServiceTemplateBuilder}. This test class check that builder will + * create ClientHttpRequestMessageSender (OkHttp3ClientHttpRequestFactory) if apache + * client is not present in the classpath + * + * @author Dmytro Nosan + */ +@RunWith(ModifiedClassPathRunner.class) +@ClassPathExclusions("httpclient-*.jar") +public class WebServiceTemplateBuilderOkHttp3ClientHttpRequestFactoryTests { + + private WebServiceTemplateBuilder builder = new WebServiceTemplateBuilder(); + + @Test + public void build() { + + WebServiceTemplate webServiceTemplate = this.builder.build(); + + assertThat(webServiceTemplate.getMessageSenders()).hasSize(1); + assertThat(webServiceTemplate.getMessageSenders()[0]) + .isInstanceOf(ClientHttpRequestMessageSender.class); + + ClientHttpRequestMessageSender sender = (ClientHttpRequestMessageSender) webServiceTemplate + .getMessageSenders()[0]; + + assertThat(sender.getRequestFactory()) + .isInstanceOf(OkHttp3ClientHttpRequestFactory.class); + + } + + @Test + public void setTimeout() { + OkHttp3ClientHttpRequestFactory factory = new OkHttp3ClientHttpRequestFactory(); + ClientHttpRequestMessageSender sender = new ClientHttpRequestMessageSender( + new BufferingClientHttpRequestFactory(factory)); + + this.builder.setConnectionTimeout(5000).setReadTimeout(2000) + .setWebServiceMessageSender(() -> sender).build(); + + OkHttpClient client = (OkHttpClient) ReflectionTestUtils.getField(factory, + "client"); + + assertThat(client).isNotNull(); + + assertThat(client.connectTimeoutMillis()).isEqualTo(5000); + assertThat(client.readTimeoutMillis()).isEqualTo(2000); + + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderSimpleClientHttpRequestFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderSimpleClientHttpRequestFactoryTests.java new file mode 100644 index 00000000000..e2f3c0a5c36 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderSimpleClientHttpRequestFactoryTests.java @@ -0,0 +1,52 @@ +/* + * 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.springframework.http.client.BufferingClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.ws.transport.http.ClientHttpRequestMessageSender; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link WebServiceTemplateBuilder}. + * + * @author Dmytro Nosan + */ +public class WebServiceTemplateBuilderSimpleClientHttpRequestFactoryTests { + + private WebServiceTemplateBuilder builder = new WebServiceTemplateBuilder(); + + @Test + public void setTimeout() { + SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); + ClientHttpRequestMessageSender sender = new ClientHttpRequestMessageSender( + new BufferingClientHttpRequestFactory(factory)); + + this.builder.setConnectionTimeout(5000).setReadTimeout(2000) + .setWebServiceMessageSender(() -> sender).build(); + + assertThat(ReflectionTestUtils.getField(factory, "connectTimeout")) + .isEqualTo(5000); + assertThat(ReflectionTestUtils.getField(factory, "readTimeout")).isEqualTo(2000); + + } + +} 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 00000000000..ffdffd40401 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/webservices/client/WebServiceTemplateBuilderTests.java @@ -0,0 +1,520 @@ +/* + * 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.function.Supplier; + +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.sax.SAXTransformerFactory; + +import org.junit.Test; +import org.mockito.Mockito; + +import org.springframework.oxm.jaxb.Jaxb2Marshaller; +import org.springframework.ws.client.core.FaultMessageResolver; +import org.springframework.ws.client.core.WebServiceTemplate; +import org.springframework.ws.client.support.interceptor.ClientInterceptor; +import org.springframework.ws.soap.client.core.SoapFaultMessageResolver; +import org.springframework.ws.soap.saaj.SaajSoapMessageFactory; +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; + +/** + * Tests for {@link WebServiceTemplateBuilder}. + * + * @author Dmytro Nosan + */ +public class WebServiceTemplateBuilderTests { + + private WebServiceTemplateBuilder builder = new WebServiceTemplateBuilder(); + + @Test + public void addInterceptors() { + ClientInterceptor f1 = Mockito.mock(ClientInterceptor.class); + ClientInterceptor f2 = Mockito.mock(ClientInterceptor.class); + + WebServiceTemplate webServiceTemplate = this.builder.addInterceptors(f1) + .addInterceptors(f2).build(); + + assertThat(webServiceTemplate.getInterceptors()).containsExactlyInAnyOrder(f1, + f2); + } + + @Test + public void addInterceptorsCollection() { + ClientInterceptor f1 = Mockito.mock(ClientInterceptor.class); + ClientInterceptor f2 = Mockito.mock(ClientInterceptor.class); + + WebServiceTemplate webServiceTemplate = this.builder + .addInterceptors(Collections.singletonList(f1)) + .addInterceptors(Collections.singleton(f2)).build(); + + assertThat(webServiceTemplate.getInterceptors()).containsExactlyInAnyOrder(f1, + f2); + + } + + @Test + public void setInterceptors() { + ClientInterceptor f1 = Mockito.mock(ClientInterceptor.class); + ClientInterceptor f2 = Mockito.mock(ClientInterceptor.class); + + WebServiceTemplate webServiceTemplate = this.builder.setInterceptors(f1) + .setInterceptors(f2).build(); + + assertThat(webServiceTemplate.getInterceptors()).doesNotContain(f1).contains(f2); + } + + @Test + public void setInterceptorsCollection() { + ClientInterceptor f1 = Mockito.mock(ClientInterceptor.class); + ClientInterceptor f2 = Mockito.mock(ClientInterceptor.class); + + WebServiceTemplate webServiceTemplate = this.builder + .setInterceptors(Collections.singletonList(f1)) + .setInterceptors(Collections.singleton(f2)).build(); + + assertThat(webServiceTemplate.getInterceptors()).doesNotContain(f1).contains(f2); + + } + + @Test + public void addCustomizers() { + Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller(); + WebServiceTemplateCustomizer customizer = (ws) -> ws + .setMarshaller(jaxb2Marshaller); + WebServiceTemplateCustomizer customizer1 = (ws) -> ws + .setUnmarshaller(jaxb2Marshaller); + + WebServiceTemplate webServiceTemplate = this.builder.addCustomizers(customizer) + .addCustomizers(customizer1).build(); + + assertThat(webServiceTemplate.getMarshaller()).isEqualTo(jaxb2Marshaller); + assertThat(webServiceTemplate.getUnmarshaller()).isEqualTo(jaxb2Marshaller); + + } + + @Test + public void addCustomizersCollection() { + Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller(); + WebServiceTemplateCustomizer customizer = (ws) -> ws + .setMarshaller(jaxb2Marshaller); + WebServiceTemplateCustomizer customizer1 = (ws) -> ws + .setUnmarshaller(jaxb2Marshaller); + + WebServiceTemplate webServiceTemplate = this.builder + .addCustomizers(Collections.singleton(customizer)) + .addCustomizers(Collections.singletonList(customizer1)).build(); + + assertThat(webServiceTemplate.getMarshaller()).isEqualTo(jaxb2Marshaller); + assertThat(webServiceTemplate.getUnmarshaller()).isEqualTo(jaxb2Marshaller); + } + + @Test + public void setCustomizers() { + Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller(); + WebServiceTemplateCustomizer customizer = (ws) -> ws + .setMarshaller(jaxb2Marshaller); + WebServiceTemplateCustomizer customizer1 = (ws) -> ws + .setUnmarshaller(jaxb2Marshaller); + + WebServiceTemplate webServiceTemplate = this.builder.setCustomizers(customizer) + .setCustomizers(customizer1).build(); + + assertThat(webServiceTemplate.getMarshaller()).isNull(); + assertThat(webServiceTemplate.getUnmarshaller()).isEqualTo(jaxb2Marshaller); + + } + + @Test + public void setCustomizersCollection() { + Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller(); + WebServiceTemplateCustomizer customizer = (ws) -> ws + .setMarshaller(jaxb2Marshaller); + WebServiceTemplateCustomizer customizer1 = (ws) -> ws + .setUnmarshaller(jaxb2Marshaller); + + WebServiceTemplate webServiceTemplate = this.builder + .setCustomizers(Collections.singleton(customizer)) + .setCustomizers(Collections.singletonList(customizer1)).build(); + + assertThat(webServiceTemplate.getMarshaller()).isNull(); + assertThat(webServiceTemplate.getUnmarshaller()).isEqualTo(jaxb2Marshaller); + } + + @Test + public void addWebServiceMessageSenders() { + WebServiceMessageSender sender = Mockito.mock(WebServiceMessageSender.class); + WebServiceMessageSender sender1 = Mockito.mock(WebServiceMessageSender.class); + + WebServiceTemplate webServiceTemplate = this.builder + .addWebServiceMessageSenders(Collections.singleton(() -> sender)) + .addWebServiceMessageSenders(Collections.singletonList(() -> sender1)) + .build(); + + assertThat(webServiceTemplate.getMessageSenders()) + .containsExactlyInAnyOrder(sender, sender1); + } + + @Test + public void setWebServiceMessageSenders() { + WebServiceMessageSender sender = Mockito.mock(WebServiceMessageSender.class); + WebServiceMessageSender sender1 = Mockito.mock(WebServiceMessageSender.class); + + WebServiceTemplate webServiceTemplate = this.builder + .setWebServiceMessageSenders(Collections.singleton(() -> sender)) + .setWebServiceMessageSenders(Collections.singletonList(() -> sender1)) + .build(); + + assertThat(webServiceTemplate.getMessageSenders()).doesNotContain(sender) + .contains(sender1); + + } + + @Test + public void addWebServiceMessageSenderClass() { + + WebServiceTemplate webServiceTemplate = this.builder + .addWebServiceMessageSender(ClientHttpRequestMessageSender.class) + .addWebServiceMessageSender(HttpUrlConnectionMessageSender.class).build(); + + assertThat(webServiceTemplate.getMessageSenders()).hasSize(2); + + assertThat(webServiceTemplate.getMessageSenders()[0]) + .isInstanceOf(ClientHttpRequestMessageSender.class); + assertThat(webServiceTemplate.getMessageSenders()[1]) + .isInstanceOf(HttpUrlConnectionMessageSender.class); + } + + @Test + public void setWebServiceMessageSenderClass() { + + WebServiceTemplate webServiceTemplate = this.builder + .setWebServiceMessageSender(ClientHttpRequestMessageSender.class) + .setWebServiceMessageSender(HttpUrlConnectionMessageSender.class).build(); + + assertThat(webServiceTemplate.getMessageSenders()).hasSize(1); + assertThat(webServiceTemplate.getMessageSenders()[0]) + .isInstanceOf(HttpUrlConnectionMessageSender.class); + + } + + @Test + public void addWebServiceMessageSender() { + WebServiceMessageSender sender = Mockito.mock(WebServiceMessageSender.class); + WebServiceMessageSender sender1 = Mockito.mock(WebServiceMessageSender.class); + + WebServiceTemplate webServiceTemplate = this.builder + .addWebServiceMessageSender(() -> sender) + .addWebServiceMessageSender(() -> sender1).build(); + + assertThat(webServiceTemplate.getMessageSenders()) + .containsExactlyInAnyOrder(sender, sender1); + } + + @Test + public void setWebServiceMessageSender() { + WebServiceMessageSender sender = Mockito.mock(WebServiceMessageSender.class); + WebServiceMessageSender sender1 = Mockito.mock(WebServiceMessageSender.class); + + WebServiceTemplate webServiceTemplate = this.builder + .setWebServiceMessageSender(() -> sender) + .setWebServiceMessageSender(() -> sender1).build(); + + assertThat(webServiceTemplate.getMessageSenders()).doesNotContain(sender) + .contains(sender1); + + } + + @Test + public void setCheckConnectionForFault() { + MockWebServiceTemplate webServiceTemplate = this.builder + .setCheckConnectionForFault(false).build(MockWebServiceTemplate.class); + + assertThat(webServiceTemplate.isCheckConnectionForFault()).isFalse(); + } + + @Test + public void setCheckConnectionForError() { + + MockWebServiceTemplate webServiceTemplate = this.builder + .setCheckConnectionForError(false).build(MockWebServiceTemplate.class); + + assertThat(webServiceTemplate.isCheckConnectionForError()).isFalse(); + + } + + @Test + public void setTransformerFactoryClass() { + MockWebServiceTemplate webServiceTemplate = this.builder + .setTransformerFactoryClass(SAXTransformerFactory.class) + .build(MockWebServiceTemplate.class); + + assertThat(webServiceTemplate.getTransformerFactoryClass()) + .isEqualTo(SAXTransformerFactory.class); + + } + + @Test + public void setWebServiceMessageFactory() { + + SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory(); + + WebServiceTemplate webServiceTemplate = this.builder + .setWebServiceMessageFactory(messageFactory).build(); + + assertThat(webServiceTemplate.getMessageFactory()).isEqualTo(messageFactory); + + } + + @Test + public void setMarshaller() { + Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller(); + + WebServiceTemplate webServiceTemplate = this.builder + .setMarshaller(jaxb2Marshaller).build(); + assertThat(webServiceTemplate.getMarshaller()).isEqualTo(jaxb2Marshaller); + } + + @Test + public void setUnmarshaller() { + Jaxb2Marshaller jaxb2Unmarshaller = new Jaxb2Marshaller(); + + WebServiceTemplate webServiceTemplate = this.builder + .setUnmarshaller(jaxb2Unmarshaller).build(); + + assertThat(webServiceTemplate.getUnmarshaller()).isEqualTo(jaxb2Unmarshaller); + } + + @Test + public void setFaultMessageResolver() { + + FaultMessageResolver faultMessageResolver = new SoapFaultMessageResolver(); + 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() { + URI uri = URI.create("http://localhost:8080"); + + WebServiceTemplate webServiceTemplate = this.builder + .setDestinationProvider(() -> uri).build(); + + assertThat(webServiceTemplate.getDestinationProvider().getDestination()) + .isEqualTo(uri); + + } + + @Test + public void shouldNotOverrideDefaultSender() { + WebServiceMessageSender sender = Mockito.mock(WebServiceMessageSender.class); + WebServiceTemplate webServiceTemplate = new WebServiceTemplate(); + webServiceTemplate.setMessageSender(sender); + + this.builder.detectWebServiceMessageSender(false).configure(webServiceTemplate); + + assertThat(webServiceTemplate.getMessageSenders()).hasSize(1).contains(sender); + + } + + @Test + public void addInterceptorsToExistingWebServiceTemplate() { + ClientInterceptor f1 = Mockito.mock(ClientInterceptor.class); + ClientInterceptor f2 = Mockito.mock(ClientInterceptor.class); + + WebServiceTemplate webServiceTemplate = new WebServiceTemplate(); + webServiceTemplate.setInterceptors(new ClientInterceptor[] { f1 }); + + this.builder.addInterceptors(f2).configure(webServiceTemplate); + + assertThat(webServiceTemplate.getInterceptors()).containsExactlyInAnyOrder(f2, + f1); + } + + @Test(expected = IllegalArgumentException.class) + public void setInterceptorsArrayNull() { + this.builder.setInterceptors((ClientInterceptor[]) null).build(); + } + + @Test(expected = IllegalArgumentException.class) + public void setInterceptorsCollectionNull() { + this.builder.setInterceptors((Collection) null) + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void addInterceptorsArrayNull() { + this.builder.addInterceptors((ClientInterceptor[]) null).build(); + } + + @Test(expected = IllegalArgumentException.class) + public void addInterceptorsCollectionNull() { + this.builder.addInterceptors((Collection) null) + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void setCustomizersArrayNull() { + this.builder.setCustomizers((WebServiceTemplateCustomizer[]) null).build(); + } + + @Test(expected = IllegalArgumentException.class) + public void setCustomizersCollectionNull() { + this.builder + .setCustomizers((Collection) null) + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void addCustomizersArrayNull() { + this.builder + .addCustomizers((Collection) null) + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void addCustomizersCollectionNull() { + this.builder + .addCustomizers((Collection) null) + .build(); + } + + @Test(expected = IllegalArgumentException.class) + public void setWebServiceMessageSendersNull() { + this.builder.setWebServiceMessageSenders(null).build(); + } + + @Test(expected = IllegalArgumentException.class) + public void addWebServiceMessageSendersNull() { + this.builder.addWebServiceMessageSenders(null).build(); + } + + @Test(expected = IllegalArgumentException.class) + public void setWebServiceMessageSenderClassNull() { + this.builder.setWebServiceMessageSender( + (Class) null).build(); + } + + @Test(expected = IllegalArgumentException.class) + public void addWebServiceMessageSenderClassNull() { + this.builder.addWebServiceMessageSender( + (Class) null).build(); + } + + @Test(expected = IllegalArgumentException.class) + public void setWebServiceMessageSenderSupplierNull() { + this.builder.setWebServiceMessageSender( + (Supplier) null).build(); + } + + @Test(expected = IllegalArgumentException.class) + public void addWebServiceMessageSenderSupplierNull() { + this.builder.addWebServiceMessageSender( + (Supplier) null).build(); + } + + @Test(expected = IllegalArgumentException.class) + public void setWebServiceMessageFactoryNull() { + this.builder.setWebServiceMessageFactory(null).build(); + } + + @Test(expected = IllegalArgumentException.class) + public void setUnmarshallerNull() { + this.builder.setUnmarshaller(null).build(); + } + + @Test(expected = IllegalArgumentException.class) + public void setMarshallerNull() { + this.builder.setMarshaller(null).build(); + } + + @Test(expected = IllegalArgumentException.class) + public void setTransformerFactoryClassNull() { + this.builder.setTransformerFactoryClass(null).build(); + } + + @Test(expected = IllegalArgumentException.class) + public void setDefaultUriNull() { + this.builder.setDefaultUri(null).build(); + } + + @Test(expected = IllegalArgumentException.class) + public void setDestinationProviderNull() { + this.builder.setDestinationProvider(null).build(); + } + + private static class MockWebServiceTemplate extends WebServiceTemplate { + + private boolean checkConnectionForError; + + private boolean checkConnectionForFault; + + private Class transformerFactoryClass; + + boolean isCheckConnectionForError() { + return this.checkConnectionForError; + } + + @Override + public void setCheckConnectionForError(boolean checkConnectionForError) { + this.checkConnectionForError = checkConnectionForError; + } + + boolean isCheckConnectionForFault() { + return this.checkConnectionForFault; + } + + @Override + public void setCheckConnectionForFault(boolean checkConnectionForFault) { + this.checkConnectionForFault = checkConnectionForFault; + } + + Class getTransformerFactoryClass() { + return this.transformerFactoryClass; + } + + @Override + public void setTransformerFactoryClass( + Class transformerFactoryClass) { + this.transformerFactoryClass = transformerFactoryClass; + } + + } + +}