diff --git a/src/main/java/org/springframework/hateoas/IanaRels.java b/src/main/java/org/springframework/hateoas/IanaRels.java new file mode 100644 index 00000000..d01a0ba6 --- /dev/null +++ b/src/main/java/org/springframework/hateoas/IanaRels.java @@ -0,0 +1,59 @@ +/* + * Copyright 2013 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.hateoas; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; + +/** + * Static class to find out whether a relation type is defined by the IANA. + * + * @see http://www.iana.org/assignments/link-relations/link-relations.xhtml + * @author Oliver Gierke + */ +public class IanaRels { + + private static final Collection RELS; + + static { + + Collection rels = new HashSet(); + + rels.addAll(Arrays.asList("about", "alternate", "appendix", "archives", "author", "bookmark", "canonical", + "chapter", "collection", "contents", "copyright", "create-form", "current", "describedby", "describes", + "disclosure", "duplicate", "edit", "edit-form", "edit-media", "enclosure", "first", "glossary", "help", + "hosts", "hub", "icon", "index", "item", "last", "latest-version", "license", "lrdd", "memento", "monitor", + "monitor-group", "next", "next-archive", "nofollow", "noreferrer", "original", "payment", + "predecessor-version", "prefetch", "prev", "preview", "previous", "prev-archive", "privacy-policy", "profile", + "related", "replies", "search", "section", "self", "service", "start", "stylesheet", "subsection", + "successor-version", "tag", "terms-of-service", "timegate", "timemap", "type", "up", "version-history", "via", + "working-copy", "working-copy-of")); + + RELS = Collections.unmodifiableCollection(rels); + } + + /** + * Returns whether the given relation type is defined by the IANA. + * + * @param rel the relation type to check + * @return + */ + public static boolean isIanaRel(String rel) { + return rel == null ? false : RELS.contains(rel); + } +} diff --git a/src/main/java/org/springframework/hateoas/config/HypermediaSupportBeanDefinitionRegistrar.java b/src/main/java/org/springframework/hateoas/config/HypermediaSupportBeanDefinitionRegistrar.java index 9ea1128d..7fd3c1e7 100644 --- a/src/main/java/org/springframework/hateoas/config/HypermediaSupportBeanDefinitionRegistrar.java +++ b/src/main/java/org/springframework/hateoas/config/HypermediaSupportBeanDefinitionRegistrar.java @@ -23,6 +23,7 @@ import java.util.Map; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.BeanPostProcessor; @@ -42,6 +43,7 @@ import org.springframework.hateoas.core.DefaultLinkDiscoverer; import org.springframework.hateoas.core.DefaultRelProvider; import org.springframework.hateoas.core.DelegatingRelProvider; import org.springframework.hateoas.core.EvoInflectorRelProvider; +import org.springframework.hateoas.hal.CurieProvider; import org.springframework.hateoas.hal.HalLinkDiscoverer; import org.springframework.hateoas.hal.Jackson1HalModule; import org.springframework.hateoas.hal.Jackson2HalModule; @@ -63,6 +65,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; * * @author Oliver Gierke */ +@SuppressWarnings("deprecation") class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { private static final String LINK_DISCOVERER_BEAN_NAME = "_linkDiscoverer"; @@ -172,7 +175,8 @@ class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRe private BeanFactory factory; - /* (non-Javadoc) + /* + * (non-Javadoc) * @see org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org.springframework.beans.factory.BeanFactory) */ @Override @@ -226,7 +230,16 @@ class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRe ObjectMapper mapper = (ObjectMapper) objectMapper; mapper.registerModule(new Jackson2HalModule()); - mapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(provider)); + mapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(provider, getCurieProvider())); + } + + private CurieProvider getCurieProvider() { + + try { + return factory.getBean(CurieProvider.class); + } catch (NoSuchBeanDefinitionException e) { + return null; + } } } @@ -236,6 +249,7 @@ class HypermediaSupportBeanDefinitionRegistrar implements ImportBeanDefinitionRe * * @author Oliver Gierke */ + @Deprecated private static class Jackson1ModuleRegisteringBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware { private BeanFactory beanFactory; diff --git a/src/main/java/org/springframework/hateoas/hal/CurieProvider.java b/src/main/java/org/springframework/hateoas/hal/CurieProvider.java new file mode 100644 index 00000000..a98f9ad3 --- /dev/null +++ b/src/main/java/org/springframework/hateoas/hal/CurieProvider.java @@ -0,0 +1,44 @@ +/* + * Copyright 2013 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.hateoas.hal; + +import org.springframework.hateoas.Link; + +/** + * API to provide HAL curie information for links. + * + * @see http://tools.ietf.org/html/draft-kelly-json-hal#section-8.2 + * @author Oliver Gierke + */ +public interface CurieProvider { + + /** + * Returns the rel to be rendered for the given {@link Link}. Will potentially prefix the rel but also might decide + * not to, depending on the actual rel. + * + * @param link + * @return + */ + String getNamespacedRelFrom(Link link); + + /** + * Returns an object to render as the base curie information. Implementations have to make sure, the retunred + * instances renders as defined in the spec. + * + * @return must not be {@literal null}. + */ + Object getCurieInformation(); +} diff --git a/src/main/java/org/springframework/hateoas/hal/DefaultCurieProvider.java b/src/main/java/org/springframework/hateoas/hal/DefaultCurieProvider.java new file mode 100644 index 00000000..a537d3c0 --- /dev/null +++ b/src/main/java/org/springframework/hateoas/hal/DefaultCurieProvider.java @@ -0,0 +1,95 @@ +/* + * Copyright 2013 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.hateoas.hal; + +import org.springframework.hateoas.IanaRels; +import org.springframework.hateoas.Link; +import org.springframework.util.Assert; +import org.springframework.web.util.UriTemplate; + +/** + * @author Oliver Gierke + */ +public class DefaultCurieProvider implements CurieProvider { + + private final Curie curie; + + /** + * Creates a new {@link DefaultCurieProvider} for the given name and {@link UriTemplate}. + * + * @param name must not be {@literal null} or empty. + * @param uriTemplate must not be {@literal null} and contain exactly one template variable. + */ + public DefaultCurieProvider(String name, UriTemplate uriTemplate) { + + Assert.hasText(name, "Name must not be null or empty!"); + Assert.notNull(uriTemplate, "UriTemplate must not be null!"); + Assert.isTrue(uriTemplate.getVariableNames().size() == 1, + String.format("Expected a single template variable in the UriTemplate %s!", uriTemplate.toString())); + + this.curie = new Curie(name, uriTemplate.toString()); + } + + /* + * (non-Javadoc) + * @see org.springframework.hateoas.hal.CurieProvider#getCurieInformation() + */ + @Override + public Curie getCurieInformation() { + return curie; + } + + /* + * (non-Javadoc) + * @see org.springframework.hateoas.hal.CurieProvider#getNamespacedRelFrom(org.springframework.hateoas.Link) + */ + @Override + public String getNamespacedRelFrom(Link link) { + + String rel = link.getRel(); + + boolean prefixingNeeded = !IanaRels.isIanaRel(rel) && !rel.contains(":"); + return prefixingNeeded ? String.format("%s:%s", curie.name, rel) : rel; + } + + /** + * Value object to get the curie {@link Link} rendered in JSON. + * + * @author Oliver Gierke + */ + @SuppressWarnings("unused") + private static class Curie extends Link { + + private static final long serialVersionUID = 1L; + + private final String name; + private final boolean templated = true; + + public Curie(String name, String href) { + + super(href, "curies"); + this.name = name; + } + + public String getName() { + return name; + } + + public boolean isTemplated() { + return templated; + } + } +} diff --git a/src/main/java/org/springframework/hateoas/hal/Jackson1HalModule.java b/src/main/java/org/springframework/hateoas/hal/Jackson1HalModule.java index 2649247b..77769fe8 100644 --- a/src/main/java/org/springframework/hateoas/hal/Jackson1HalModule.java +++ b/src/main/java/org/springframework/hateoas/hal/Jackson1HalModule.java @@ -66,9 +66,11 @@ import org.springframework.util.StringUtils; /** * Jackson 1 module implementation to render {@link Link} and {@link ResourceSupport} instances in HAL compatible JSON. * + * @deprecated use Jackson 2 instead * @author Alexander Baetz * @author Oliver Gierke */ +@Deprecated public class Jackson1HalModule extends SimpleModule { /** diff --git a/src/main/java/org/springframework/hateoas/hal/Jackson2HalModule.java b/src/main/java/org/springframework/hateoas/hal/Jackson2HalModule.java index 32c69131..0895df5e 100644 --- a/src/main/java/org/springframework/hateoas/hal/Jackson2HalModule.java +++ b/src/main/java/org/springframework/hateoas/hal/Jackson2HalModule.java @@ -17,6 +17,7 @@ package org.springframework.hateoas.hal; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; @@ -91,15 +92,17 @@ public class Jackson2HalModule extends SimpleModule { public static class HalLinkListSerializer extends ContainerSerializer> implements ContextualSerializer { private final BeanProperty property; + private final CurieProvider curieProvider; - public HalLinkListSerializer() { - this(null); + public HalLinkListSerializer(CurieProvider curieProvider) { + this(null, curieProvider); } - public HalLinkListSerializer(BeanProperty property) { + public HalLinkListSerializer(BeanProperty property, CurieProvider curieProvider) { super(List.class, false); this.property = property; + this.curieProvider = curieProvider; } /* @@ -113,17 +116,28 @@ public class Jackson2HalModule extends SimpleModule { JsonGenerationException { // sort links according to their relation - Map> sortedLinks = new LinkedHashMap>(); + Map> sortedLinks = new LinkedHashMap>(); + boolean prefixingRequired = curieProvider != null; + for (Link link : value) { - if (sortedLinks.get(link.getRel()) == null) { - sortedLinks.put(link.getRel(), new ArrayList()); + + String rel = prefixingRequired ? curieProvider.getNamespacedRelFrom(link) : link.getRel(); + + if (sortedLinks.get(rel) == null) { + sortedLinks.put(rel, new ArrayList()); } - sortedLinks.get(link.getRel()).add(link); + + sortedLinks.get(rel).add(link); + } + + if (prefixingRequired) { + Object curieInformation = curieProvider.getCurieInformation(); + sortedLinks.put("curies", Arrays.asList(curieInformation)); } TypeFactory typeFactory = provider.getConfig().getTypeFactory(); JavaType keyType = typeFactory.uncheckedSimpleType(String.class); - JavaType valueType = typeFactory.constructCollectionType(ArrayList.class, Link.class); + JavaType valueType = typeFactory.constructCollectionType(ArrayList.class, Object.class); JavaType mapType = typeFactory.constructMapType(HashMap.class, keyType, valueType); MapSerializer serializer = MapSerializer.construct(new String[] {}, mapType, true, null, @@ -141,7 +155,7 @@ public class Jackson2HalModule extends SimpleModule { @Override public JsonSerializer createContextual(SerializerProvider provider, BeanProperty property) throws JsonMappingException { - return new HalLinkListSerializer(property); + return new HalLinkListSerializer(property, curieProvider); } /* @@ -294,7 +308,7 @@ public class Jackson2HalModule extends SimpleModule { ContextualSerializer { private final BeanProperty property; - private JsonSerializer serializer; + private final Map, JsonSerializer> serializers; public OptionalListJackson2Serializer() { this(null); @@ -309,13 +323,12 @@ public class Jackson2HalModule extends SimpleModule { super(List.class, false); this.property = property; + this.serializers = new HashMap, JsonSerializer>(); } /* * (non-Javadoc) - * - * @see com.fasterxml.jackson.databind.ser.ContainerSerializer#_withValueTypeSerializer(com.fasterxml.jackson.databind.jsontype. - * TypeSerializer) + * @see com.fasterxml.jackson.databind.ser.ContainerSerializer#_withValueTypeSerializer(com.fasterxml.jackson.databind.jsontype.TypeSerializer) */ @Override public ContainerSerializer _withValueTypeSerializer(TypeSerializer vts) { @@ -324,9 +337,7 @@ public class Jackson2HalModule extends SimpleModule { /* * (non-Javadoc) - * - * @see com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, - * com.fasterxml.jackson.databind.SerializerProvider) + * @see com.fasterxml.jackson.databind.ser.std.StdSerializer#serialize(java.lang.Object, com.fasterxml.jackson.core.JsonGenerator, com.fasterxml.jackson.databind.SerializerProvider) */ @Override public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, @@ -334,6 +345,10 @@ public class Jackson2HalModule extends SimpleModule { List list = (List) value; + if (list.isEmpty()) { + return; + } + if (list.size() == 1) { serializeContents(list.iterator(), jgen, provider); return; @@ -352,27 +367,35 @@ public class Jackson2HalModule extends SimpleModule { if (elem == null) { provider.defaultSerializeNull(jgen); } else { - if (serializer == null) { - serializer = provider.findValueSerializer(elem.getClass(), property); - } - serializer.serialize(elem, jgen, provider); + getOrLookupSerializerFor(elem.getClass(), provider).serialize(elem, jgen, provider); } } } - /* - * (non-Javadoc) - * - * @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentSerializer() - */ - @Override - public JsonSerializer getContentSerializer() { + private JsonSerializer getOrLookupSerializerFor(Class type, SerializerProvider provider) + throws JsonMappingException { + + JsonSerializer serializer = serializers.get(type); + + if (serializer == null) { + serializer = provider.findValueSerializer(type, property); + serializers.put(type, serializer); + } + return serializer; } /* * (non-Javadoc) - * + * @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentSerializer() + */ + @Override + public JsonSerializer getContentSerializer() { + return null; + } + + /* + * (non-Javadoc) * @see com.fasterxml.jackson.databind.ser.ContainerSerializer#getContentType() */ @Override @@ -561,10 +584,11 @@ public class Jackson2HalModule extends SimpleModule { private final Map, Object> instanceMap = new HashMap, Object>(); - public HalHandlerInstantiator(RelProvider resolver) { + public HalHandlerInstantiator(RelProvider resolver, CurieProvider curieProvider) { Assert.notNull(resolver, "RelProvider must not be null!"); - this.instanceMap.put(HalResourcesSerializer.class, new HalResourcesSerializer(null, resolver)); + this.instanceMap.put(HalResourcesSerializer.class, new HalResourcesSerializer(resolver)); + this.instanceMap.put(HalLinkListSerializer.class, new HalLinkListSerializer(curieProvider)); } private Object findInstance(Class type) { diff --git a/src/test/java/org/springframework/hateoas/PagedResourcesMarshallingTest.java b/src/test/java/org/springframework/hateoas/PagedResourcesMarshallingTest.java index 0ba6601a..866bab37 100644 --- a/src/test/java/org/springframework/hateoas/PagedResourcesMarshallingTest.java +++ b/src/test/java/org/springframework/hateoas/PagedResourcesMarshallingTest.java @@ -72,9 +72,7 @@ public class PagedResourcesMarshallingTest { assertThat(actual, is(pagedResources)); } - public static class Inner { - - } + public static class Inner {} private static String readFile(org.springframework.core.io.Resource resource) throws IOException { diff --git a/src/test/java/org/springframework/hateoas/VndErrorsMarshallingTest.java b/src/test/java/org/springframework/hateoas/VndErrorsMarshallingTest.java index ae6c6af2..50311f3d 100644 --- a/src/test/java/org/springframework/hateoas/VndErrorsMarshallingTest.java +++ b/src/test/java/org/springframework/hateoas/VndErrorsMarshallingTest.java @@ -37,6 +37,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.core.io.ClassPathResource; import org.springframework.hateoas.VndErrors.VndError; +import org.springframework.hateoas.core.EvoInflectorRelProvider; import org.springframework.hateoas.hal.Jackson1HalModule; import org.springframework.hateoas.hal.Jackson2HalModule; @@ -54,6 +55,8 @@ public class VndErrorsMarshallingTest { Marshaller marshaller; Unmarshaller unmarshaller; + RelProvider relProvider = new EvoInflectorRelProvider(); + VndErrors errors; String jsonReference; String xmlReference; @@ -65,6 +68,7 @@ public class VndErrorsMarshallingTest { } @Before + @SuppressWarnings("deprecation") public void setUp() throws Exception { jackson1Mapper = new ObjectMapper(); @@ -73,6 +77,7 @@ public class VndErrorsMarshallingTest { jackson2Mapper = new com.fasterxml.jackson.databind.ObjectMapper(); jackson2Mapper.registerModule(new Jackson2HalModule()); + jackson2Mapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider, null)); jackson2Mapper.configure(SerializationFeature.INDENT_OUTPUT, true); JAXBContext context = JAXBContext.newInstance(VndErrors.class); diff --git a/src/test/java/org/springframework/hateoas/config/EnableHypermediaSupportIntegrationTest.java b/src/test/java/org/springframework/hateoas/config/EnableHypermediaSupportIntegrationTest.java index d857b264..bda13802 100644 --- a/src/test/java/org/springframework/hateoas/config/EnableHypermediaSupportIntegrationTest.java +++ b/src/test/java/org/springframework/hateoas/config/EnableHypermediaSupportIntegrationTest.java @@ -52,6 +52,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; public class EnableHypermediaSupportIntegrationTest { @Test + @SuppressWarnings("deprecation") public void bootstrapHalConfiguration() { ApplicationContext context = new AnnotationConfigApplicationContext(HalConfig.class); diff --git a/src/test/java/org/springframework/hateoas/hal/DefaultCurieProviderUnitTests.java b/src/test/java/org/springframework/hateoas/hal/DefaultCurieProviderUnitTests.java new file mode 100644 index 00000000..0f7597e8 --- /dev/null +++ b/src/test/java/org/springframework/hateoas/hal/DefaultCurieProviderUnitTests.java @@ -0,0 +1,75 @@ +/* + * Copyright 2013 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.hateoas.hal; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import org.junit.Test; +import org.springframework.hateoas.Link; +import org.springframework.web.util.UriTemplate; + +/** + * Unit tests for {@link DefaultCurieProvider}. + * + * @author Oliver Gierke + */ +public class DefaultCurieProviderUnitTests { + + private static final UriTemplate URI_TEMPLATE = new UriTemplate("http://localhost:8080/rels/{rel}"); + + CurieProvider provider = new DefaultCurieProvider("acme", URI_TEMPLATE); + + @Test(expected = IllegalArgumentException.class) + public void preventsNullCurieName() { + new DefaultCurieProvider(null, URI_TEMPLATE); + } + + @Test(expected = IllegalArgumentException.class) + public void preventsEmptyCurieName() { + new DefaultCurieProvider("", URI_TEMPLATE); + } + + @Test(expected = IllegalArgumentException.class) + public void preventsNullUriTemplateName() { + new DefaultCurieProvider("acme", null); + } + + @Test(expected = IllegalArgumentException.class) + public void preventsUriTemplateWithoutVariable() { + new DefaultCurieProvider("acme", new UriTemplate("http://localhost:8080/rels")); + } + + @Test(expected = IllegalArgumentException.class) + public void preventsUriTemplateWithMoreThanOneVariable() { + new DefaultCurieProvider("acme", new UriTemplate("http://localhost:8080/rels/{rel}/{another}")); + } + + @Test + public void doesNotPrefixIanaRels() { + assertThat(provider.getNamespacedRelFrom(new Link("http://amazon.com")), is("self")); + } + + @Test + public void prefixesNormalRels() { + assertThat(provider.getNamespacedRelFrom(new Link("http://amazon.com", "book")), is("acme:book")); + } + + @Test + public void doesNotPrefixQualifiedRels() { + assertThat(provider.getNamespacedRelFrom(new Link("http://amazon.com", "custom:rel")), is("custom:rel")); + } +} diff --git a/src/test/java/org/springframework/hateoas/hal/Jackson2HalIntegrationTest.java b/src/test/java/org/springframework/hateoas/hal/Jackson2HalIntegrationTest.java index d074b2f8..d7133051 100644 --- a/src/test/java/org/springframework/hateoas/hal/Jackson2HalIntegrationTest.java +++ b/src/test/java/org/springframework/hateoas/hal/Jackson2HalIntegrationTest.java @@ -19,6 +19,7 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.junit.Before; @@ -33,6 +34,9 @@ import org.springframework.hateoas.ResourceSupport; import org.springframework.hateoas.Resources; import org.springframework.hateoas.core.AnnotationRelProvider; import org.springframework.hateoas.hal.Jackson2HalModule.HalHandlerInstantiator; +import org.springframework.web.util.UriTemplate; + +import com.fasterxml.jackson.databind.ObjectMapper; /** * Integration tests for Jackson 2 HAL integration. @@ -56,11 +60,13 @@ public class Jackson2HalIntegrationTest extends AbstractJackson2MarshallingInteg static final Links PAGINATION_LINKS = new Links(new Link("foo", Link.REL_NEXT), new Link("bar", Link.REL_PREVIOUS)); + static final String CURIED_DOCUMENT = "{\"_links\":{\"self\":{\"href\":\"foo\"},\"foo:myrel\":{\"href\":\"bar\"},\"curies\":{\"href\":\"htp://localhost:8080/rels/{rel}\",\"name\":\"foo\",\"templated\":true}},\"_embedded\":{}}"; + @Before public void setUpModule() { mapper.registerModule(new Jackson2HalModule()); - mapper.setHandlerInstantiator(new HalHandlerInstantiator(new AnnotationRelProvider())); + mapper.setHandlerInstantiator(new HalHandlerInstantiator(new AnnotationRelProvider(), null)); } /** @@ -245,11 +251,17 @@ public class Jackson2HalIntegrationTest extends AbstractJackson2MarshallingInteg assertThat(result, is(setupAnnotatedResources())); } + /** + * @see #63 + */ @Test public void serializesPagedResource() throws Exception { assertThat(write(setupAnnotatedPagedResources()), is(ANNOTATED_PAGED_RESOURCES)); } + /** + * @see #64 + */ @Test public void deserializesPagedResource() throws Exception { PagedResources> result = mapper.readValue( @@ -260,6 +272,24 @@ public class Jackson2HalIntegrationTest extends AbstractJackson2MarshallingInteg assertThat(result, is(setupAnnotatedPagedResources())); } + /** + * @see #126 + */ + @Test + public void rendersCuriesCorrectly() throws Exception { + + CurieProvider curieProvider = new DefaultCurieProvider("foo", new UriTemplate("htp://localhost:8080/rels/{rel}")); + + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(new Jackson2HalModule()); + mapper.setHandlerInstantiator(new HalHandlerInstantiator(new AnnotationRelProvider(), curieProvider)); + + Resources resources = new Resources(Collections.emptySet(), new Link("foo"), new Link("bar", + "myrel")); + + assertThat(mapper.writeValueAsString(resources), is(CURIED_DOCUMENT)); + } + private static Resources> setupAnnotatedPagedResources() { List> content = new ArrayList>();