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 7916e31195a..855734d8ff4 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 @@ -44,7 +44,7 @@ import org.springframework.http.converter.HttpMessageConverter; import org.springframework.util.Assert; import org.springframework.util.concurrent.ListenableFuture; import org.springframework.util.concurrent.ListenableFutureAdapter; -import org.springframework.web.util.DefaultUriTemplateHandler; +import org.springframework.web.util.AbstractUriTemplateHandler; import org.springframework.web.util.UriTemplateHandler; /** @@ -166,9 +166,9 @@ public class AsyncRestTemplate extends InterceptingAsyncHttpAccessor implements */ public void setDefaultUriVariables(Map defaultUriVariables) { UriTemplateHandler handler = this.syncTemplate.getUriTemplateHandler(); - Assert.isInstanceOf(DefaultUriTemplateHandler.class, handler, + Assert.isInstanceOf(AbstractUriTemplateHandler.class, handler, "Can only use this property in conjunction with a DefaultUriTemplateHandler."); - ((DefaultUriTemplateHandler) handler).setDefaultUriVariables(defaultUriVariables); + ((AbstractUriTemplateHandler) handler).setDefaultUriVariables(defaultUriVariables); } /** 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 c700c8dae84..322f97193af 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 @@ -51,6 +51,7 @@ import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConve 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.UriTemplateHandler; @@ -250,9 +251,9 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat * @since 4.3 */ public void setDefaultUriVariables(Map defaultUriVariables) { - Assert.isInstanceOf(DefaultUriTemplateHandler.class, this.uriTemplateHandler, - "Can only use this property in conjunction with a DefaultUriTemplateHandler."); - ((DefaultUriTemplateHandler) this.uriTemplateHandler).setDefaultUriVariables(defaultUriVariables); + Assert.isInstanceOf(AbstractUriTemplateHandler.class, this.uriTemplateHandler, + "Can only use this property in conjunction with an AbstractUriTemplateHandler."); + ((AbstractUriTemplateHandler) this.uriTemplateHandler).setDefaultUriVariables(defaultUriVariables); } /** 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 new file mode 100644 index 00000000000..09ebe5f1ec5 --- /dev/null +++ b/spring-web/src/main/java/org/springframework/web/util/AbstractUriTemplateHandler.java @@ -0,0 +1,134 @@ +/* + * Copyright 2002-2016 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.web.util; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.util.Assert; + +/** + * Abstract base class for {@link UriTemplateHandler} implementations. + * + *

Support {@link #setBaseUrl} and {@link #setDefaultUriVariables} properties + * that should be relevant regardless of the URI template expand and encode + * mechanism used in sub-classes. + * + * @author Rossen Stoyanchev + * @since 4.3 + */ +public abstract class AbstractUriTemplateHandler implements UriTemplateHandler { + + private String baseUrl; + + private final Map defaultUriVariables = new HashMap(); + + + /** + * Configure a base URL to prepend URI templates with. The base URL must + * have a scheme and host but may optionally contain a port and a path. + * The base URL must be fully expanded and encoded which can be done via + * {@link UriComponentsBuilder}. + * @param baseUrl the base URL. + */ + public void setBaseUrl(String baseUrl) { + if (baseUrl != null) { + UriComponents uriComponents = UriComponentsBuilder.fromUriString(baseUrl).build(); + Assert.hasText(uriComponents.getScheme(), "'baseUrl' must have a scheme"); + Assert.hasText(uriComponents.getHost(), "'baseUrl' must have a host"); + Assert.isNull(uriComponents.getQuery(), "'baseUrl' cannot have a query"); + Assert.isNull(uriComponents.getFragment(), "'baseUrl' cannot have a fragment"); + } + this.baseUrl = baseUrl; + } + + /** + * Return the configured base URL. + */ + public String getBaseUrl() { + return this.baseUrl; + } + + /** + * Configure default URI variable values to use with every expanded URI + * template. These default values apply only when expanding with a Map, and + * not with an array, where the Map supplied to {@link #expand(String, Map)} + * can override the default values. + * @param defaultUriVariables the default URI variable values + * @since 4.3 + */ + public void setDefaultUriVariables(Map defaultUriVariables) { + this.defaultUriVariables.clear(); + if (defaultUriVariables != null) { + this.defaultUriVariables.putAll(defaultUriVariables); + } + } + + /** + * Return a read-only copy of the configured default URI variables. + */ + public Map getDefaultUriVariables() { + return Collections.unmodifiableMap(this.defaultUriVariables); + } + + + @Override + public URI expand(String uriTemplate, Map uriVariables) { + if (!getDefaultUriVariables().isEmpty()) { + Map map = new HashMap(); + map.putAll(getDefaultUriVariables()); + map.putAll(uriVariables); + uriVariables = map; + } + URI url = expandInternal(uriTemplate, uriVariables); + return insertBaseUrl(url); + } + + @Override + public URI expand(String uriTemplate, Object... uriVariables) { + URI url = expandInternal(uriTemplate, uriVariables); + return insertBaseUrl(url); + } + + /** + * Actually expand and encode the URI template. + */ + protected abstract URI expandInternal(String uriTemplate, Map uriVariables); + + /** + * Actually expand and encode the URI template. + */ + protected abstract URI expandInternal(String uriTemplate, Object... uriVariables); + + /** + * Insert a base URL (if configured) unless the given URL has a host already. + */ + private URI insertBaseUrl(URI url) { + try { + if (getBaseUrl() != null && url.getHost() == null) { + url = new URI(getBaseUrl() + url.toString()); + } + return url; + } + catch (URISyntaxException ex) { + throw new IllegalArgumentException("Invalid URL after inserting base URL: " + url, ex); + } + } + +} 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 90d94713e94..34f0baa5500 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 @@ -19,13 +19,10 @@ package org.springframework.web.util; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.springframework.util.Assert; - /** * Default implementation of {@link UriTemplateHandler} based on the use of * {@link UriComponentsBuilder} for expanding and encoding variables. @@ -39,64 +36,13 @@ import org.springframework.util.Assert; * @author Rossen Stoyanchev * @since 4.2 */ -public class DefaultUriTemplateHandler implements UriTemplateHandler { - - private String baseUrl; - - private final Map defaultUriVariables = new HashMap(); +public class DefaultUriTemplateHandler extends AbstractUriTemplateHandler { private boolean parsePath; private boolean strictEncoding; - /** - * Configure a base URL to prepend URI templates with. The base URL must - * have a scheme and host but may optionally contain a port and a path. - * The base URL must be fully expanded and encoded which can be done via - * {@link UriComponentsBuilder}. - * @param baseUrl the base URL. - */ - public void setBaseUrl(String baseUrl) { - if (baseUrl != null) { - UriComponents uriComponents = UriComponentsBuilder.fromUriString(baseUrl).build(); - Assert.hasText(uriComponents.getScheme(), "'baseUrl' must have a scheme"); - Assert.hasText(uriComponents.getHost(), "'baseUrl' must have a host"); - Assert.isNull(uriComponents.getQuery(), "'baseUrl' cannot have a query"); - Assert.isNull(uriComponents.getFragment(), "'baseUrl' cannot have a fragment"); - } - this.baseUrl = baseUrl; - } - - /** - * Return the configured base URL. - */ - public String getBaseUrl() { - return this.baseUrl; - } - - /** - * Configure default URI variable values to use with every expanded URI - * template. These default values apply only when expanding with a Map, and - * not with an array, where the Map supplied to {@link #expand(String, Map)} - * can override the default values. - * @param defaultUriVariables the default URI variable values - * @since 4.3 - */ - public void setDefaultUriVariables(Map defaultUriVariables) { - this.defaultUriVariables.clear(); - if (defaultUriVariables != null) { - this.defaultUriVariables.putAll(defaultUriVariables); - } - } - - /** - * Return a read-only copy of the configured default URI variables. - */ - public Map getDefaultUriVariables() { - return Collections.unmodifiableMap(this.defaultUriVariables); - } - /** * Whether to parse the path of a URI template string into path segments. *

If set to {@code true} the URI template path is immediately decomposed @@ -146,23 +92,23 @@ public class DefaultUriTemplateHandler implements UriTemplateHandler { @Override - public URI expand(String uriTemplate, Map uriVariables) { + public URI expandInternal(String uriTemplate, Map uriVariables) { UriComponentsBuilder uriComponentsBuilder = initUriComponentsBuilder(uriTemplate); UriComponents uriComponents = expandAndEncode(uriComponentsBuilder, uriVariables); - return insertBaseUrl(uriComponents); + return createUri(uriComponents); } @Override - public URI expand(String uriTemplate, Object... uriVariables) { + public URI expandInternal(String uriTemplate, Object... uriVariables) { UriComponentsBuilder uriComponentsBuilder = initUriComponentsBuilder(uriTemplate); UriComponents uriComponents = expandAndEncode(uriComponentsBuilder, uriVariables); - return insertBaseUrl(uriComponents); + return createUri(uriComponents); } /** - * Create a {@code UriComponentsBuilder} from the UriTemplate string. The - * default implementation also parses the path into path segments if - * {@link #setParsePath parsePath} is enabled. + * Create a {@code UriComponentsBuilder} from the URI template string. + * This implementation also breaks up the path into path segments depending + * on whether {@link #setParsePath parsePath} is enabled. */ protected UriComponentsBuilder initUriComponentsBuilder(String uriTemplate) { UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(uriTemplate); @@ -177,36 +123,28 @@ public class DefaultUriTemplateHandler implements UriTemplateHandler { } protected UriComponents expandAndEncode(UriComponentsBuilder builder, Map uriVariables) { - // Simple scenario: use the input map - if (getDefaultUriVariables().isEmpty() && !isStrictEncoding()) { - return builder.build().expand(uriVariables).encode(); - } - // Create a new map - Map variablesToUse = new HashMap(); - variablesToUse.putAll(getDefaultUriVariables()); - variablesToUse.putAll(uriVariables); - if (!isStrictEncoding()) { - return builder.build().expand(variablesToUse).encode(); + return builder.buildAndExpand(uriVariables).encode(); } else { - for (Map.Entry entry : variablesToUse.entrySet()) { - variablesToUse.put(entry.getKey(), applyStrictEncoding(entry.getValue())); + Map encodedUriVars = new HashMap(uriVariables.size()); + for (Map.Entry entry : uriVariables.entrySet()) { + encodedUriVars.put(entry.getKey(), applyStrictEncoding(entry.getValue())); } - return builder.build().expand(variablesToUse); + return builder.buildAndExpand(encodedUriVars); } } protected UriComponents expandAndEncode(UriComponentsBuilder builder, Object[] uriVariables) { if (!isStrictEncoding()) { - return builder.build().expand(uriVariables).encode(); + return builder.buildAndExpand(uriVariables).encode(); } else { Object[] encodedUriVars = new Object[uriVariables.length]; for (int i = 0; i < uriVariables.length; i++) { encodedUriVars[i] = applyStrictEncoding(uriVariables[i]); } - return builder.build().expand(encodedUriVars); + return builder.buildAndExpand(encodedUriVars); } } @@ -221,22 +159,12 @@ public class DefaultUriTemplateHandler implements UriTemplateHandler { } } - /** - * Invoked after the URI template has been expanded and encoded to prefix it - * with the configured {@link #setBaseUrl(String) baseUrl}, if any. - * @param uriComponents the expanded and encoded URI - * @return the final URI - */ - protected URI insertBaseUrl(UriComponents uriComponents) { - String url = uriComponents.toUriString(); - if (getBaseUrl() != null && uriComponents.getHost() == null) { - url = getBaseUrl() + url; - } + private URI createUri(UriComponents uriComponents) { try { - return new URI(url); + return new URI(uriComponents.toUriString()); } catch (URISyntaxException ex) { - throw new IllegalArgumentException("Invalid URL after inserting base URL: " + url, ex); + throw new IllegalStateException("Could not create URI object: " + ex.getMessage(), ex); } }