diff --git a/src/main/java/org/springframework/hateoas/client/Hop.java b/src/main/java/org/springframework/hateoas/client/Hop.java index 8c4fd742..44862423 100644 --- a/src/main/java/org/springframework/hateoas/client/Hop.java +++ b/src/main/java/org/springframework/hateoas/client/Hop.java @@ -25,6 +25,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import org.springframework.http.HttpHeaders; import org.springframework.util.Assert; /** @@ -49,6 +50,8 @@ public class Hop { */ private final @Wither Map parameters; + private final @Wither HttpHeaders headers; + /** * Creates a new {@link Hop} for the given relation name. * @@ -59,7 +62,7 @@ public class Hop { Assert.hasText(rel, "Relation must not be null or empty!"); - return new Hop(rel, Collections. emptyMap()); + return new Hop(rel, Collections. emptyMap(), null); } /** @@ -76,7 +79,33 @@ public class Hop { HashMap parameters = new HashMap(this.parameters); parameters.put(name, value); - return new Hop(rel, parameters); + if (this.hasHeaders()) + return new Hop(rel, parameters, this.headers); + else + return new Hop(rel, parameters, null); + } + + /** + * Add one header to the HttpHeaders collection. + * + * @param headerName must not be {@literal null} or empty. + * @param headerValue can be {@literal null}. + * @return + */ + public Hop withHeader(String headerName, String headerValue){ + Assert.notNull(headers, "Headers must not be null!"); + + HttpHeaders headers = new HttpHeaders(); + + if(this.hasHeaders()) + headers.addAll(this.headers); + + headers.add(headerName, headerValue); + + if(this.hasParameters()) + return new Hop(rel, this.parameters, headers); + else + return new Hop(rel, Collections. emptyMap(), headers); } /** @@ -88,6 +117,15 @@ public class Hop { return !this.parameters.isEmpty(); } + /** + * Returns whether the {@link Hop} has headers declared. + * + * @return + */ + boolean hasHeaders() { + return this.headers != null && !this.headers.isEmpty(); + } + /** * Create a new {@link Map} starting with the supplied template parameters. Then add the ones for this hop. This * allows a local hop to override global parameters. @@ -106,4 +144,20 @@ public class Hop { return mergedParameters; } + + /** + * Create a new {@link Map} starting with the supplied headers. Then add the ones for this hop. This + * allows a local hop to override global headers. + * + * @param globalHeaders must not be {@literal null}. + * @return + */ + HttpHeaders getMergedHeaders(HttpHeaders globalHeaders){ + Assert.notNull(globalHeaders, "Global Headers must not be null!"); + + if(this.hasHeaders()) + globalHeaders.addAll(this.headers); + + return globalHeaders; + } } diff --git a/src/main/java/org/springframework/hateoas/client/Traverson.java b/src/main/java/org/springframework/hateoas/client/Traverson.java index 766b005b..5c90905a 100644 --- a/src/main/java/org/springframework/hateoas/client/Traverson.java +++ b/src/main/java/org/springframework/hateoas/client/Traverson.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import com.sun.jndi.toolkit.url.Uri; import org.springframework.core.ParameterizedTypeReference; import org.springframework.hateoas.Link; import org.springframework.hateoas.LinkDiscoverer; @@ -324,7 +325,9 @@ public class Traverson { public T toObject(Class type) { Assert.notNull(type, "Target type must not be null!"); - return operations.exchange(traverseToExpandedFinalUrl(), GET, prepareRequest(headers), type).getBody(); + + LinkWithHeader linkWithHeader = traverseToExpandedFinalUrl(); + return operations.exchange(linkWithHeader.uriAsURI, GET, prepareRequest(linkWithHeader.httpHeaders), type).getBody(); } /** @@ -337,7 +340,9 @@ public class Traverson { public T toObject(ParameterizedTypeReference type) { Assert.notNull(type, "Target type must not be null!"); - return operations.exchange(traverseToExpandedFinalUrl(), GET, prepareRequest(headers), type).getBody(); + + LinkWithHeader linkWithHeader = traverseToExpandedFinalUrl(); + return operations.exchange(linkWithHeader.uriAsURI, GET, prepareRequest(linkWithHeader.httpHeaders), type).getBody(); } /** @@ -351,7 +356,8 @@ public class Traverson { Assert.hasText(jsonPath, "JSON path must not be null or empty!"); - String forObject = operations.exchange(traverseToExpandedFinalUrl(), GET, prepareRequest(headers), String.class) + LinkWithHeader linkWithHeader = traverseToExpandedFinalUrl(); + String forObject = operations.exchange(linkWithHeader.uriAsURI, GET, prepareRequest(linkWithHeader.httpHeaders), String.class) .getBody(); return JsonPath.read(forObject, jsonPath); } @@ -365,7 +371,8 @@ public class Traverson { public ResponseEntity toEntity(Class type) { Assert.notNull(type, "Target type must not be null!"); - return operations.exchange(traverseToExpandedFinalUrl(), GET, prepareRequest(headers), type); + LinkWithHeader linkWithHeader = traverseToExpandedFinalUrl(); + return operations.exchange(linkWithHeader.uriAsURI, GET, prepareRequest(linkWithHeader.httpHeaders), type); } /** @@ -393,26 +400,24 @@ public class Traverson { private Link traverseToLink(boolean expandFinalUrl) { Assert.isTrue(rels.size() > 0, "At least one rel needs to be provided!"); - return new Link(expandFinalUrl ? traverseToExpandedFinalUrl().toString() : traverseToFinalUrl(), + return new Link(expandFinalUrl ? traverseToExpandedFinalUrl().uriAsURI.toString() : traverseToFinalUrl(), rels.get(rels.size() - 1).getRel()); } private String traverseToFinalUrl() { - String uri = getAndFindLinkWithRel(baseUri.toString(), rels.iterator()); - return new UriTemplate(uri).toString(); + return getAndFindLinkWithRel(baseUri.toString(), rels.iterator(), headers).uriAsString; } - private URI traverseToExpandedFinalUrl() { + private LinkWithHeader traverseToExpandedFinalUrl() { - String uri = getAndFindLinkWithRel(baseUri.toString(), rels.iterator()); - return new UriTemplate(uri).expand(templateParameters); + return getAndFindLinkWithRel(baseUri.toString(), rels.iterator(), headers); } - private String getAndFindLinkWithRel(String uri, Iterator rels) { + private LinkWithHeader getAndFindLinkWithRel(String uri, Iterator rels, HttpHeaders headers) { if (!rels.hasNext()) { - return uri; + return new LinkWithHeader(uri, headers); } HttpEntity request = prepareRequest(headers); @@ -436,9 +441,45 @@ public class Traverson { * Don't expand if the parameters are empty */ if (!thisHop.hasParameters()) { - return getAndFindLinkWithRel(link.getHref(), rels); + return getAndFindLinkWithRel(link.getHref(), rels, thisHop.getMergedHeaders(this.headers)); } else { - return getAndFindLinkWithRel(link.expand(thisHop.getMergedParameters(templateParameters)).getHref(), rels); + return getAndFindLinkWithRel(link.expand(thisHop.getMergedParameters(templateParameters)).getHref(), rels, thisHop.getMergedHeaders(this.headers)); + } + } + + private class LinkWithHeader{ + private String uriAsString; + private URI uriAsURI; + private HttpHeaders httpHeaders; + + LinkWithHeader(String uri, HttpHeaders httpHeaders){ + this.uriAsString = new UriTemplate(uri).toString(); + this.uriAsURI = new UriTemplate(uri).expand(templateParameters); + this.httpHeaders = httpHeaders; + } + + public URI getUriAsURI() { + return uriAsURI; + } + + public void setUriAsURI(URI uriAsURI) { + this.uriAsURI = uriAsURI; + } + + public String getUriAsString() { + return uriAsString; + } + + public void setUriAsString(String uriAsString) { + this.uriAsString = uriAsString; + } + + public HttpHeaders getHttpHeaders() { + return httpHeaders; + } + + public void setHttpHeaders(HttpHeaders httpHeaders) { + this.httpHeaders = httpHeaders; } } }