001/*
002 * Copyright 2012-2017 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.test.web.client;
018
019import java.util.Collections;
020import java.util.Map;
021import java.util.concurrent.ConcurrentHashMap;
022
023import org.springframework.beans.BeanUtils;
024import org.springframework.boot.web.client.RestTemplateBuilder;
025import org.springframework.boot.web.client.RestTemplateCustomizer;
026import org.springframework.test.web.client.MockRestServiceServer;
027import org.springframework.test.web.client.RequestExpectationManager;
028import org.springframework.test.web.client.SimpleRequestExpectationManager;
029import org.springframework.util.Assert;
030import org.springframework.web.client.RestTemplate;
031
032/**
033 * {@link RestTemplateCustomizer} that can be applied to a {@link RestTemplateBuilder}
034 * instances to add {@link MockRestServiceServer} support.
035 * <p>
036 * Typically applied to an existing builder before it is used, for example:
037 * <pre class="code">
038 * MockServerRestTemplateCustomizer customizer = new MockServerRestTemplateCustomizer();
039 * MyBean bean = new MyBean(new RestTemplateBuilder(customizer));
040 * customizer.getServer().expect(requestTo("/hello")).andRespond(withSuccess());
041 * bean.makeRestCall();
042 * </pre>
043 * <p>
044 * If the customizer is only used once, the {@link #getServer()} method can be used to
045 * obtain the mock server. If the customizer has been used more than once the
046 * {@link #getServer(RestTemplate)} or {@link #getServers()} method must be used to access
047 * the related server.
048 *
049 * @author Phillip Webb
050 * @since 1.4.0
051 * @see #getServer()
052 * @see #getServer(RestTemplate)
053 */
054public class MockServerRestTemplateCustomizer implements RestTemplateCustomizer {
055
056        private Map<RestTemplate, RequestExpectationManager> expectationManagers = new ConcurrentHashMap<>();
057
058        private Map<RestTemplate, MockRestServiceServer> servers = new ConcurrentHashMap<>();
059
060        private final Class<? extends RequestExpectationManager> expectationManager;
061
062        private boolean detectRootUri = true;
063
064        public MockServerRestTemplateCustomizer() {
065                this.expectationManager = SimpleRequestExpectationManager.class;
066        }
067
068        public MockServerRestTemplateCustomizer(
069                        Class<? extends RequestExpectationManager> expectationManager) {
070                Assert.notNull(expectationManager, "ExpectationManager must not be null");
071                this.expectationManager = expectationManager;
072        }
073
074        /**
075         * Set if root URIs from {@link RootUriRequestExpectationManager} should be detected
076         * and applied to the {@link MockRestServiceServer}.
077         * @param detectRootUri if root URIs should be detected
078         */
079        public void setDetectRootUri(boolean detectRootUri) {
080                this.detectRootUri = detectRootUri;
081        }
082
083        @Override
084        public void customize(RestTemplate restTemplate) {
085                RequestExpectationManager expectationManager = createExpectationManager();
086                if (this.detectRootUri) {
087                        expectationManager = RootUriRequestExpectationManager
088                                        .forRestTemplate(restTemplate, expectationManager);
089                }
090                MockRestServiceServer server = MockRestServiceServer.bindTo(restTemplate)
091                                .build(expectationManager);
092                this.expectationManagers.put(restTemplate, expectationManager);
093                this.servers.put(restTemplate, server);
094        }
095
096        protected RequestExpectationManager createExpectationManager() {
097                return BeanUtils.instantiateClass(this.expectationManager);
098        }
099
100        public MockRestServiceServer getServer() {
101                Assert.state(!this.servers.isEmpty(),
102                                "Unable to return a single MockRestServiceServer since "
103                                                + "MockServerRestTemplateCustomizer has not been bound to "
104                                                + "a RestTemplate");
105                Assert.state(this.servers.size() == 1,
106                                "Unable to return a single MockRestServiceServer since "
107                                                + "MockServerRestTemplateCustomizer has been bound to "
108                                                + "more than one RestTemplate");
109                return this.servers.values().iterator().next();
110        }
111
112        public Map<RestTemplate, RequestExpectationManager> getExpectationManagers() {
113                return this.expectationManagers;
114        }
115
116        public MockRestServiceServer getServer(RestTemplate restTemplate) {
117                return this.servers.get(restTemplate);
118        }
119
120        public Map<RestTemplate, MockRestServiceServer> getServers() {
121                return Collections.unmodifiableMap(this.servers);
122        }
123
124}