001/*
002 * Copyright 2012-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.boot.webservices.client;
018
019import java.lang.reflect.Method;
020import java.time.Duration;
021import java.util.function.Supplier;
022
023import org.springframework.boot.web.client.ClientHttpRequestFactorySupplier;
024import org.springframework.http.client.ClientHttpRequestFactory;
025import org.springframework.util.Assert;
026import org.springframework.util.ReflectionUtils;
027import org.springframework.ws.transport.WebServiceMessageSender;
028import org.springframework.ws.transport.http.ClientHttpRequestMessageSender;
029
030/**
031 * {@link WebServiceMessageSender} builder that can detect a suitable HTTP library based
032 * on the classpath.
033 *
034 * @author Stephane Nicoll
035 * @since 2.1.0
036 */
037public class HttpWebServiceMessageSenderBuilder {
038
039        private Duration connectTimeout;
040
041        private Duration readTimeout;
042
043        private Supplier<ClientHttpRequestFactory> requestFactorySupplier;
044
045        /**
046         * Set the connection timeout.
047         * @param connectTimeout the connection timeout
048         * @return a new builder instance
049         */
050        public HttpWebServiceMessageSenderBuilder setConnectTimeout(Duration connectTimeout) {
051                this.connectTimeout = connectTimeout;
052                return this;
053        }
054
055        /**
056         * Set the read timeout.
057         * @param readTimeout the read timeout
058         * @return a new builder instance
059         */
060        public HttpWebServiceMessageSenderBuilder setReadTimeout(Duration readTimeout) {
061                this.readTimeout = readTimeout;
062                return this;
063        }
064
065        /**
066         * Set the {@code Supplier} of {@link ClientHttpRequestFactory} that should be called
067         * to create the HTTP-based {@link WebServiceMessageSender}.
068         * @param requestFactorySupplier the supplier for the request factory
069         * @return a new builder instance
070         */
071        public HttpWebServiceMessageSenderBuilder requestFactory(
072                        Supplier<ClientHttpRequestFactory> requestFactorySupplier) {
073                Assert.notNull(requestFactorySupplier,
074                                "RequestFactory Supplier must not be null");
075                this.requestFactorySupplier = requestFactorySupplier;
076                return this;
077        }
078
079        public WebServiceMessageSender build() {
080                ClientHttpRequestFactory requestFactory = (this.requestFactorySupplier != null)
081                                ? this.requestFactorySupplier.get()
082                                : new ClientHttpRequestFactorySupplier().get();
083                if (this.connectTimeout != null) {
084                        new TimeoutRequestFactoryCustomizer(this.connectTimeout, "setConnectTimeout")
085                                        .customize(requestFactory);
086                }
087                if (this.readTimeout != null) {
088                        new TimeoutRequestFactoryCustomizer(this.readTimeout, "setReadTimeout")
089                                        .customize(requestFactory);
090                }
091                return new ClientHttpRequestMessageSender(requestFactory);
092        }
093
094        /**
095         * {@link ClientHttpRequestFactory} customizer to call a "set timeout" method.
096         */
097        private static class TimeoutRequestFactoryCustomizer {
098
099                private final Duration timeout;
100
101                private final String methodName;
102
103                TimeoutRequestFactoryCustomizer(Duration timeout, String methodName) {
104                        this.timeout = timeout;
105                        this.methodName = methodName;
106                }
107
108                public void customize(ClientHttpRequestFactory factory) {
109                        ReflectionUtils.invokeMethod(findMethod(factory), factory,
110                                        Math.toIntExact(this.timeout.toMillis()));
111                }
112
113                private Method findMethod(ClientHttpRequestFactory factory) {
114                        Method method = ReflectionUtils.findMethod(factory.getClass(),
115                                        this.methodName, int.class);
116                        if (method != null) {
117                                return method;
118                        }
119                        throw new IllegalStateException("Request factory " + factory.getClass()
120                                        + " does not have a " + this.methodName + "(int) method");
121                }
122
123        }
124
125}