From d057c3d981730c398c3a68cf0d71c9dfff69ba3b Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Fri, 27 Jan 2017 16:38:16 -0500 Subject: [PATCH] Deprecate DefaultUriTemplate handler Following on the introduction of the UriBuilderFactory and its DefaultUriBuilderFactory implementation, this commit deprecates DefaultUriTemplate (and AbstractUriTemplateHandler). The new DefaultUriBuilderFactory has comparable functionality and is more flexible but cannot be merged into the existing hierarchy and be backwards compatible with regards to protected methods. Issue: SPR-15124 --- .../web/client/AsyncRestTemplate.java | 14 ++++-- .../web/client/RestTemplate.java | 25 +++++++---- .../web/util/AbstractUriTemplateHandler.java | 2 + .../web/util/DefaultUriTemplateHandler.java | 2 + .../web/util/HierarchicalUriComponents.java | 5 ++- .../web/util/UriTemplateHandler.java | 12 ++--- .../web/client/RestTemplateTests.java | 7 ++- .../util/DefaultUriBuilderFactoryTests.java | 44 ++++++++++++++----- 8 files changed, 74 insertions(+), 37 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java b/spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java index 351c46e7ad..00c9a87677 100644 --- a/spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java +++ b/spring-web/src/main/java/org/springframework/web/client/AsyncRestTemplate.java @@ -45,6 +45,7 @@ import org.springframework.util.Assert; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFutureAdapter; import org.springframework.web.util.AbstractUriTemplateHandler; +import org.springframework.web.util.DefaultUriBuilderFactory; import org.springframework.web.util.UriTemplateHandler; /** @@ -163,9 +164,16 @@ public class AsyncRestTemplate extends InterceptingAsyncHttpAccessor implements */ public void setDefaultUriVariables(Map defaultUriVariables) { UriTemplateHandler handler = this.syncTemplate.getUriTemplateHandler(); - Assert.isInstanceOf(AbstractUriTemplateHandler.class, handler, - "Can only use this property in conjunction with a DefaultUriTemplateHandler"); - ((AbstractUriTemplateHandler) handler).setDefaultUriVariables(defaultUriVariables); + if (handler instanceof DefaultUriBuilderFactory) { + ((DefaultUriBuilderFactory) handler).setDefaultUriVariables(defaultUriVariables); + } + else if (handler instanceof AbstractUriTemplateHandler) { + ((AbstractUriTemplateHandler) handler).setDefaultUriVariables(defaultUriVariables); + } + else { + throw new IllegalArgumentException( + "This property is not supported with the configured UriTemplateHandler."); + } } /** diff --git a/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java b/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java index 316a5d2c80..22721dc212 100644 --- a/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java +++ b/spring-web/src/main/java/org/springframework/web/client/RestTemplate.java @@ -53,7 +53,7 @@ import org.springframework.http.converter.xml.SourceHttpMessageConverter; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.web.util.AbstractUriTemplateHandler; -import org.springframework.web.util.DefaultUriTemplateHandler; +import org.springframework.web.util.DefaultUriBuilderFactory; import org.springframework.web.util.UriTemplateHandler; /** @@ -149,7 +149,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler(); - private UriTemplateHandler uriTemplateHandler = new DefaultUriTemplateHandler(); + private UriTemplateHandler uriTemplateHandler = new DefaultUriBuilderFactory(); private final ResponseExtractor headersExtractor = new HeadersExtractor(); @@ -254,24 +254,31 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat /** * Configure default URI variable values. This is a shortcut for: *
-	 * DefaultUriTemplateHandler handler = new DefaultUriTemplateHandler();
+	 * DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory();
 	 * handler.setDefaultUriVariables(...);
 	 *
 	 * RestTemplate restTemplate = new RestTemplate();
 	 * restTemplate.setUriTemplateHandler(handler);
 	 * 
- * @param defaultUriVariables the default URI variable values + * @param uriVars the default URI variable values * @since 4.3 */ - public void setDefaultUriVariables(Map defaultUriVariables) { - Assert.isInstanceOf(AbstractUriTemplateHandler.class, this.uriTemplateHandler, - "Can only use this property in conjunction with an AbstractUriTemplateHandler"); - ((AbstractUriTemplateHandler) this.uriTemplateHandler).setDefaultUriVariables(defaultUriVariables); + public void setDefaultUriVariables(Map uriVars) { + if (this.uriTemplateHandler instanceof DefaultUriBuilderFactory) { + ((DefaultUriBuilderFactory) this.uriTemplateHandler).setDefaultUriVariables(uriVars); + } + else if (this.uriTemplateHandler instanceof AbstractUriTemplateHandler) { + ((AbstractUriTemplateHandler) this.uriTemplateHandler).setDefaultUriVariables(uriVars); + } + else { + throw new IllegalArgumentException( + "This property is not supported with the configured UriTemplateHandler."); + } } /** * Configure the {@link UriTemplateHandler} to use to expand URI templates. - * By default the {@link DefaultUriTemplateHandler} is used which relies on + * By default the {@link DefaultUriBuilderFactory} is used which relies on * Spring's URI template support and exposes several useful properties that * customize its behavior for encoding and for prepending a common base URL. * An alternative implementation may be used to plug an external URI diff --git a/spring-web/src/main/java/org/springframework/web/util/AbstractUriTemplateHandler.java b/spring-web/src/main/java/org/springframework/web/util/AbstractUriTemplateHandler.java index b4a8cbb318..d8a88c4637 100644 --- a/spring-web/src/main/java/org/springframework/web/util/AbstractUriTemplateHandler.java +++ b/spring-web/src/main/java/org/springframework/web/util/AbstractUriTemplateHandler.java @@ -33,7 +33,9 @@ import org.springframework.util.Assert; * * @author Rossen Stoyanchev * @since 4.3 + * @deprecated as of 5.0 in favor of {@link DefaultUriBuilderFactory} */ +@Deprecated public abstract class AbstractUriTemplateHandler implements UriTemplateHandler { private String baseUrl; diff --git a/spring-web/src/main/java/org/springframework/web/util/DefaultUriTemplateHandler.java b/spring-web/src/main/java/org/springframework/web/util/DefaultUriTemplateHandler.java index 8c0ad71504..9ad9357a30 100644 --- a/spring-web/src/main/java/org/springframework/web/util/DefaultUriTemplateHandler.java +++ b/spring-web/src/main/java/org/springframework/web/util/DefaultUriTemplateHandler.java @@ -33,7 +33,9 @@ import java.util.Map; * * @author Rossen Stoyanchev * @since 4.2 + * @deprecated as of 5.0 in favor of {@link DefaultUriBuilderFactory} */ +@Deprecated public class DefaultUriTemplateHandler extends AbstractUriTemplateHandler { private boolean parsePath; diff --git a/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java b/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java index 905f29eef3..98c9a7cdd1 100644 --- a/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java +++ b/spring-web/src/main/java/org/springframework/web/util/HierarchicalUriComponents.java @@ -455,8 +455,9 @@ final class HierarchicalUriComponents extends UriComponents { if (getHost() != null) { builder.host(getHost()); } - if (getPort() != -1) { - builder.port(getPort()); + // Avoid parsing the port, may have URI variable.. + if (this.port != null) { + builder.port(this.port); } if (getPath() != null) { this.path.copyToUriComponentsBuilder(builder); diff --git a/spring-web/src/main/java/org/springframework/web/util/UriTemplateHandler.java b/spring-web/src/main/java/org/springframework/web/util/UriTemplateHandler.java index c03eb191ab..d394d34f31 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UriTemplateHandler.java +++ b/spring-web/src/main/java/org/springframework/web/util/UriTemplateHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -20,18 +20,14 @@ import java.net.URI; import java.util.Map; /** - * Strategy for expanding a URI template with full control over the URI template - * syntax and the encoding of variables. Also a convenient central point for - * pre-processing all URI templates for example to insert a common base path. + * Strategy for expanding a URI template. * *

Supported as a property on the {@code RestTemplate} as well as the - * {@code AsyncRestTemplate}. The {@link DefaultUriTemplateHandler} is built - * on Spring's URI template support via {@link UriComponentsBuilder}. An - * alternative implementation may be used to plug external URI template libraries. + * {@code AsyncRestTemplate}. * * @author Rossen Stoyanchev * @since 4.2 - * @see org.springframework.web.client.RestTemplate#setUriTemplateHandler + * @see DefaultUriBuilderFactory */ public interface UriTemplateHandler { diff --git a/spring-web/src/test/java/org/springframework/web/client/RestTemplateTests.java b/spring-web/src/test/java/org/springframework/web/client/RestTemplateTests.java index 1b3f4fe24c..1ada39213a 100644 --- a/spring-web/src/test/java/org/springframework/web/client/RestTemplateTests.java +++ b/spring-web/src/test/java/org/springframework/web/client/RestTemplateTests.java @@ -42,7 +42,7 @@ import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.converter.GenericHttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.web.util.DefaultUriTemplateHandler; +import org.springframework.web.util.DefaultUriBuilderFactory; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -83,7 +83,7 @@ public class RestTemplateTests { response = mock(ClientHttpResponse.class); errorHandler = mock(ResponseErrorHandler.class); converter = mock(HttpMessageConverter.class); - template = new RestTemplate(Collections.>singletonList(converter)); + template = new RestTemplate(Collections.singletonList(converter)); template.setRequestFactory(requestFactory); template.setErrorHandler(errorHandler); } @@ -273,8 +273,7 @@ public class RestTemplateTests { @Test public void getForObjectWithCustomUriTemplateHandler() throws Exception { - DefaultUriTemplateHandler uriTemplateHandler = new DefaultUriTemplateHandler(); - uriTemplateHandler.setParsePath(true); + DefaultUriBuilderFactory uriTemplateHandler = new DefaultUriBuilderFactory(); template.setUriTemplateHandler(uriTemplateHandler); URI expectedUri = new URI("http://example.com/hotels/1/pic/pics%2Flogo.png/size/150x150"); diff --git a/spring-web/src/test/java/org/springframework/web/util/DefaultUriBuilderFactoryTests.java b/spring-web/src/test/java/org/springframework/web/util/DefaultUriBuilderFactoryTests.java index f7931ffcc0..c7481d7de5 100644 --- a/spring-web/src/test/java/org/springframework/web/util/DefaultUriBuilderFactoryTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/DefaultUriBuilderFactoryTests.java @@ -16,12 +16,14 @@ package org.springframework.web.util; import java.net.URI; -import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import org.junit.Test; import org.springframework.web.util.DefaultUriBuilderFactory.EncodingMode; +import static java.util.Collections.singletonMap; import static junit.framework.TestCase.assertEquals; /** @@ -30,9 +32,6 @@ import static junit.framework.TestCase.assertEquals; */ public class DefaultUriBuilderFactoryTests { - private static final String baseUrl = "http://foo.com/bar"; - - @Test public void defaultSettings() throws Exception { DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(); @@ -57,27 +56,39 @@ public class DefaultUriBuilderFactoryTests { @Test public void defaultUriVars() throws Exception { DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory("http://{host}/bar"); - factory.setDefaultUriVariables(Collections.singletonMap("host", "foo.com")); - URI uri = factory.uriString("/{id}").build(Collections.singletonMap("id", "123")); + factory.setDefaultUriVariables(singletonMap("host", "foo.com")); + URI uri = factory.uriString("/{id}").build(singletonMap("id", "123")); assertEquals("http://foo.com/bar/123", uri.toString()); } @Test public void defaultUriVarsWithOverride() throws Exception { DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory("http://{host}/bar"); - factory.setDefaultUriVariables(Collections.singletonMap("host", "spring.io")); - URI uri = factory.uriString("/baz").build(Collections.singletonMap("host", "docs.spring.io")); + factory.setDefaultUriVariables(singletonMap("host", "spring.io")); + URI uri = factory.uriString("/baz").build(singletonMap("host", "docs.spring.io")); assertEquals("http://docs.spring.io/bar/baz", uri.toString()); } @Test public void defaultUriVarsWithEmptyVarArg() throws Exception { DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory("http://{host}/bar"); - factory.setDefaultUriVariables(Collections.singletonMap("host", "foo.com")); + factory.setDefaultUriVariables(singletonMap("host", "foo.com")); URI uri = factory.uriString("/baz").build(); assertEquals("Expected delegation to build(Map) method", "http://foo.com/bar/baz", uri.toString()); } + @Test + public void defaultUriVarsSpr14147() throws Exception { + Map defaultUriVars = new HashMap<>(2); + defaultUriVars.put("host", "api.example.com"); + defaultUriVars.put("port", "443"); + DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(); + factory.setDefaultUriVariables(defaultUriVars); + + URI uri = factory.expand("https://{host}:{port}/v42/customers/{id}", singletonMap("id", 123L)); + assertEquals("https://api.example.com:443/v42/customers/123", uri.toString()); + } + @Test public void encodingValuesOnly() throws Exception { DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(); @@ -88,7 +99,18 @@ public class DefaultUriBuilderFactoryTests { String expected = "/foo/a%2Fb/c%2Fd"; assertEquals(expected, uriBuilder.build(id).toString()); - assertEquals(expected, uriBuilder.build(Collections.singletonMap("id", id)).toString()); + assertEquals(expected, uriBuilder.build(singletonMap("id", id)).toString()); + } + + @Test + public void encodingValuesOnlySpr14147() throws Exception { + DefaultUriBuilderFactory factory = new DefaultUriBuilderFactory(); + factory.setEncodingMode(EncodingMode.VALUES_ONLY); + factory.setDefaultUriVariables(singletonMap("host", "www.example.com")); + UriBuilder uriBuilder = factory.uriString("http://{host}/user/{userId}/dashboard"); + + assertEquals("http://www.example.com/user/john%3Bdoe/dashboard", + uriBuilder.build(singletonMap("userId", "john;doe")).toString()); } @Test @@ -101,7 +123,7 @@ public class DefaultUriBuilderFactoryTests { String expected = "/foo/a%2Fb/c%2Fd"; assertEquals(expected, uriBuilder.build(id).toString()); - assertEquals(expected, uriBuilder.build(Collections.singletonMap("id", id)).toString()); + assertEquals(expected, uriBuilder.build(singletonMap("id", id)).toString()); } @Test