diff --git a/src/main/java/org/springframework/hateoas/config/HateoasConfiguration.java b/src/main/java/org/springframework/hateoas/config/HateoasConfiguration.java index 5ef19cc8..a609e1d6 100644 --- a/src/main/java/org/springframework/hateoas/config/HateoasConfiguration.java +++ b/src/main/java/org/springframework/hateoas/config/HateoasConfiguration.java @@ -15,15 +15,17 @@ */ package org.springframework.hateoas.config; -import org.springframework.beans.factory.BeanCreationException; +import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; -import org.springframework.context.support.MessageSourceAccessor; +import org.springframework.context.support.AbstractResourceBasedMessageSource; import org.springframework.context.support.ReloadableResourceBundleMessageSource; +import org.springframework.core.io.ClassPathResource; import org.springframework.hateoas.client.LinkDiscoverer; import org.springframework.hateoas.client.LinkDiscoverers; +import org.springframework.hateoas.mediatype.MessageResolver; import org.springframework.hateoas.server.LinkRelationProvider; import org.springframework.hateoas.server.LinkRelationProvider.LookupContext; import org.springframework.hateoas.server.core.AnnotationLinkRelationProvider; @@ -31,6 +33,7 @@ import org.springframework.hateoas.server.core.DefaultLinkRelationProvider; import org.springframework.hateoas.server.core.DelegatingLinkRelationProvider; import org.springframework.hateoas.server.core.EvoInflectorLinkRelationProvider; import org.springframework.http.MediaType; +import org.springframework.lang.Nullable; import org.springframework.plugin.core.PluginRegistry; import org.springframework.plugin.core.config.EnablePluginRegistries; import org.springframework.plugin.core.support.PluginRegistryFactoryBean; @@ -49,24 +52,9 @@ import org.springframework.util.ClassUtils; @EnablePluginRegistries({ LinkDiscoverer.class }) class HateoasConfiguration { - /** - * The {@link MessageSourceAccessor} to provide messages for {@link ResourceDescription}s being rendered. - * - * @return - */ @Bean - public MessageSourceAccessor linkRelationMessageSource() { - - try { - - ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); - messageSource.setBasename("classpath:rest-messages"); - - return new MessageSourceAccessor(messageSource); - - } catch (Exception o_O) { - throw new BeanCreationException("resourceDescriptionMessageSourceAccessor", "", o_O); - } + public MessageResolver messageResolver() { + return MessageResolver.of(lookupMessageSource()); } // RelProvider @@ -108,4 +96,25 @@ class HateoasConfiguration { LinkDiscoverers linkDiscoverers(PluginRegistry discoverers) { return new LinkDiscoverers(discoverers); } + + /** + * Creates a message source for the {@code rest-messages} resource bundle if the file exists or a + * {@link NoOpMessageSource} otherwise. + * + * @return will never be {@literal null}. + */ + @Nullable + private static final MessageSource lookupMessageSource() { + + ClassPathResource resource = new ClassPathResource("rest-messages"); + + if (!resource.exists()) { + return null; + } + + AbstractResourceBasedMessageSource messageSource = new ReloadableResourceBundleMessageSource(); + messageSource.setBasename("classpath:rest-messages"); + + return messageSource; + } } diff --git a/src/main/java/org/springframework/hateoas/mediatype/MessageResolver.java b/src/main/java/org/springframework/hateoas/mediatype/MessageResolver.java new file mode 100644 index 00000000..e0fd6a26 --- /dev/null +++ b/src/main/java/org/springframework/hateoas/mediatype/MessageResolver.java @@ -0,0 +1,51 @@ +/* + * Copyright 2019 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 + * + * https://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.mediatype; + +import org.springframework.context.MessageSource; +import org.springframework.context.MessageSourceResolvable; +import org.springframework.context.support.MessageSourceAccessor; +import org.springframework.lang.Nullable; + +/** + * A simplified variant of {@link MessageSourceAccessor} to allow more direct replacement with a no-op implementation in + * case the target {@link MessageSource} is unavailable to avoid resolution overhead. + * + * @author Oliver Drotbohm + */ +public interface MessageResolver { + + public static final MessageResolver NONE = NoMessageResolver.INSTANCE; + + /** + * Resolve the given {@link MessageSourceResolvable}. Return {@literal null} if no message was found. + * + * @param resolvable must not be {@literal null}. + * @return + */ + @Nullable + String resolve(MessageSourceResolvable resolvable); + + /** + * Obtains a {@link MessageResolver} for the given {@link MessageSource}. + * + * @param messageSource can be {@literal null}. + * @return will never be {@literal null}. + */ + public static MessageResolver of(@Nullable MessageSource messageSource) { + return messageSource == null ? NoMessageResolver.INSTANCE : new MessageSourceResolver(messageSource); + } +} diff --git a/src/main/java/org/springframework/hateoas/mediatype/MessageSourceResolver.java b/src/main/java/org/springframework/hateoas/mediatype/MessageSourceResolver.java new file mode 100644 index 00000000..726151aa --- /dev/null +++ b/src/main/java/org/springframework/hateoas/mediatype/MessageSourceResolver.java @@ -0,0 +1,58 @@ +/* + * Copyright 2019 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 + * + * https://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.mediatype; + +import org.springframework.context.MessageSource; +import org.springframework.context.MessageSourceResolvable; +import org.springframework.context.support.MessageSourceAccessor; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * A {@link MessageResolver} based on a {@link MessageSource}. + * + * @author Oliver Drotbohm + */ +class MessageSourceResolver implements MessageResolver { + + private final MessageSourceAccessor accessor; + + /** + * Creates a new {@link MessageSourceResolver} for the given {@link MessageSource}. + * + * @param messageSource must not be {@literal null}. + */ + MessageSourceResolver(MessageSource messageSource) { + + Assert.notNull(messageSource, "MessageSource must not be null!"); + + this.accessor = new MessageSourceAccessor(messageSource); + } + + /* + * (non-Javadoc) + * @see org.springframework.hateoas.mediatype.MessageResolver#resolve(org.springframework.context.MessageSourceResolvable) + */ + @Nullable + @Override + public String resolve(MessageSourceResolvable resolvable) { + + String resolved = accessor.getMessage(resolvable); + + return StringUtils.hasText(resolved) ? resolved : null; + } +} diff --git a/src/main/java/org/springframework/hateoas/mediatype/NoMessageResolver.java b/src/main/java/org/springframework/hateoas/mediatype/NoMessageResolver.java new file mode 100644 index 00000000..4d34d5bb --- /dev/null +++ b/src/main/java/org/springframework/hateoas/mediatype/NoMessageResolver.java @@ -0,0 +1,34 @@ +/* + * Copyright 2019 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 + * + * https://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.mediatype; + +import org.springframework.context.MessageSourceResolvable; +import org.springframework.lang.Nullable; + +enum NoMessageResolver implements MessageResolver { + + INSTANCE; + + /* + * (non-Javadoc) + * @see org.springframework.hateoas.mediatype.MessageResolver#resolve(org.springframework.context.MessageSourceResolvable) + */ + @Nullable + @Override + public String resolve(MessageSourceResolvable resolvable) { + return null; + } +} diff --git a/src/main/java/org/springframework/hateoas/mediatype/hal/HalMediaTypeConfiguration.java b/src/main/java/org/springframework/hateoas/mediatype/hal/HalMediaTypeConfiguration.java index 8b3ee4a6..a1b2ce61 100644 --- a/src/main/java/org/springframework/hateoas/mediatype/hal/HalMediaTypeConfiguration.java +++ b/src/main/java/org/springframework/hateoas/mediatype/hal/HalMediaTypeConfiguration.java @@ -18,13 +18,12 @@ package org.springframework.hateoas.mediatype.hal; import java.util.List; import org.springframework.beans.factory.ObjectProvider; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.support.MessageSourceAccessor; import org.springframework.hateoas.client.LinkDiscoverer; import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType; import org.springframework.hateoas.config.HypermediaMappingInformation; +import org.springframework.hateoas.mediatype.MessageResolver; import org.springframework.hateoas.server.LinkRelationProvider; import org.springframework.http.MediaType; @@ -43,22 +42,21 @@ public class HalMediaTypeConfiguration implements HypermediaMappingInformation { private final LinkRelationProvider relProvider; private final ObjectProvider curieProvider; private final ObjectProvider halConfiguration; - private final MessageSourceAccessor messageSourceAccessor; + private final MessageResolver resolver; /** * @param relProvider * @param curieProvider * @param halConfiguration - * @param messageSourceAccessor + * @param resolver */ public HalMediaTypeConfiguration(LinkRelationProvider relProvider, ObjectProvider curieProvider, - ObjectProvider halConfiguration, - @Qualifier("linkRelationMessageSource") MessageSourceAccessor messageSourceAccessor) { + ObjectProvider halConfiguration, MessageResolver resolver) { this.relProvider = relProvider; this.curieProvider = curieProvider; this.halConfiguration = halConfiguration; - this.messageSourceAccessor = messageSourceAccessor; + this.resolver = resolver; } @Bean @@ -85,7 +83,7 @@ public class HalMediaTypeConfiguration implements HypermediaMappingInformation { mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.registerModule(new Jackson2HalModule()); mapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider, - curieProvider.getIfAvailable(() -> CurieProvider.NONE), messageSourceAccessor, + curieProvider.getIfAvailable(() -> CurieProvider.NONE), resolver, halConfiguration.getIfAvailable(HalConfiguration::new))); return mapper; diff --git a/src/main/java/org/springframework/hateoas/mediatype/hal/Jackson2HalModule.java b/src/main/java/org/springframework/hateoas/mediatype/hal/Jackson2HalModule.java index 7a26649d..0b5d46a8 100644 --- a/src/main/java/org/springframework/hateoas/mediatype/hal/Jackson2HalModule.java +++ b/src/main/java/org/springframework/hateoas/mediatype/hal/Jackson2HalModule.java @@ -27,14 +27,13 @@ import java.util.Map; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; -import org.springframework.context.NoSuchMessageException; -import org.springframework.context.support.MessageSourceAccessor; import org.springframework.hateoas.CollectionModel; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.Link; import org.springframework.hateoas.LinkRelation; import org.springframework.hateoas.Links; import org.springframework.hateoas.RepresentationModel; +import org.springframework.hateoas.mediatype.MessageResolver; import org.springframework.hateoas.mediatype.hal.HalConfiguration.RenderSingleLinks; import org.springframework.hateoas.server.LinkRelationProvider; import org.springframework.lang.NonNull; @@ -113,23 +112,23 @@ public class Jackson2HalModule extends SimpleModule { private final @Nullable BeanProperty property; private final CurieProvider curieProvider; private final EmbeddedMapper mapper; - private final MessageSourceAccessor accessor; + private final MessageResolver resolver; private final HalConfiguration halConfiguration; - public HalLinkListSerializer(CurieProvider curieProvider, EmbeddedMapper mapper, MessageSourceAccessor accessor, + public HalLinkListSerializer(CurieProvider curieProvider, EmbeddedMapper mapper, MessageResolver resolver, HalConfiguration halConfiguration) { - this(null, curieProvider, mapper, accessor, halConfiguration); + this(null, curieProvider, mapper, resolver, halConfiguration); } public HalLinkListSerializer(@Nullable BeanProperty property, CurieProvider curieProvider, EmbeddedMapper mapper, - MessageSourceAccessor accessor, HalConfiguration halConfiguration) { + MessageResolver accessor, HalConfiguration halConfiguration) { super(TypeFactory.defaultInstance().constructType(Links.class)); this.property = property; this.curieProvider = curieProvider; this.mapper = mapper; - this.accessor = accessor; + this.resolver = accessor; this.halConfiguration = halConfiguration; } @@ -206,26 +205,7 @@ public class Jackson2HalModule extends SimpleModule { HalLinkRelation rel = HalLinkRelation.of(link.getRel()); - return new HalLink(link, getTitle(rel)); - } - - /** - * Returns the title for the given local link relation resolved through the configured {@link MessageSourceAccessor} - * . - * - * @param relation must not be {@literal null} or empty. - * @return - */ - @Nullable - private String getTitle(HalLinkRelation relation) { - - Assert.notNull(relation, "Local relation must not be null or empty!"); - - try { - return accessor == null ? null : accessor.getMessage(relation); - } catch (NoSuchMessageException o_O) { - return null; - } + return new HalLink(link, resolver.resolve(rel)); } /* @@ -236,7 +216,7 @@ public class Jackson2HalModule extends SimpleModule { @SuppressWarnings("null") public JsonSerializer createContextual(SerializerProvider provider, BeanProperty property) throws JsonMappingException { - return new HalLinkListSerializer(property, curieProvider, mapper, accessor, halConfiguration); + return new HalLinkListSerializer(property, curieProvider, mapper, resolver, halConfiguration); } /* @@ -719,43 +699,43 @@ public class Jackson2HalModule extends SimpleModule { private final @Nullable AutowireCapableBeanFactory delegate; public HalHandlerInstantiator(LinkRelationProvider provider, CurieProvider curieProvider, - MessageSourceAccessor messageSourceAccessor) { - this(provider, curieProvider, messageSourceAccessor, new HalConfiguration()); + MessageResolver resolver) { + this(provider, curieProvider, resolver, new HalConfiguration()); } /** * Creates a new {@link HalHandlerInstantiator} using the given {@link LinkRelationProvider}, {@link CurieProvider} - * and {@link MessageSourceAccessor}. Registers a prepared {@link HalResourcesSerializer} and + * and {@link MessageResolver}. Registers a prepared {@link HalResourcesSerializer} and * {@link HalLinkListSerializer} falling back to instantiation expecting a default constructor. * * @param provider must not be {@literal null}. * @param curieProvider can be {@literal null}. - * @param messageSourceAccessor can be {@literal null}. + * @param resolver must not be {@literal null}. */ - public HalHandlerInstantiator(LinkRelationProvider provider, CurieProvider curieProvider, - MessageSourceAccessor messageSourceAccessor, HalConfiguration halConfiguration) { - this(provider, curieProvider, messageSourceAccessor, true, halConfiguration); + public HalHandlerInstantiator(LinkRelationProvider provider, CurieProvider curieProvider, MessageResolver resolver, + HalConfiguration halConfiguration) { + this(provider, curieProvider, resolver, true, halConfiguration); } /** * Creates a new {@link HalHandlerInstantiator} using the given {@link LinkRelationProvider}, {@link CurieProvider} - * and {@link MessageSourceAccessor} and whether to enforce embedded collections. Registers a prepared + * and {@link MessageResolver} and whether to enforce embedded collections. Registers a prepared * {@link HalResourcesSerializer} and {@link HalLinkListSerializer} falling back to instantiation expecting a * default constructor. * * @param provider must not be {@literal null}. * @param curieProvider can be {@literal null} - * @param accessor can be {@literal null}. + * @param resolver must not be {@literal null}.. * @param enforceEmbeddedCollections */ - public HalHandlerInstantiator(LinkRelationProvider provider, CurieProvider curieProvider, - MessageSourceAccessor accessor, boolean enforceEmbeddedCollections, HalConfiguration halConfiguration) { - this(provider, curieProvider, accessor, enforceEmbeddedCollections, null, halConfiguration); + public HalHandlerInstantiator(LinkRelationProvider provider, CurieProvider curieProvider, MessageResolver resolver, + boolean enforceEmbeddedCollections, HalConfiguration halConfiguration) { + this(provider, curieProvider, resolver, enforceEmbeddedCollections, null, halConfiguration); } - private HalHandlerInstantiator(LinkRelationProvider provider, CurieProvider curieProvider, - MessageSourceAccessor accessor, boolean enforceEmbeddedCollections, - @Nullable AutowireCapableBeanFactory delegate, HalConfiguration halConfiguration) { + private HalHandlerInstantiator(LinkRelationProvider provider, CurieProvider curieProvider, MessageResolver resolver, + boolean enforceEmbeddedCollections, @Nullable AutowireCapableBeanFactory delegate, + HalConfiguration halConfiguration) { Assert.notNull(provider, "RelProvider must not be null!"); Assert.notNull(curieProvider, "CurieProvider must not be null!"); @@ -766,7 +746,7 @@ public class Jackson2HalModule extends SimpleModule { this.serializers.put(HalResourcesSerializer.class, new HalResourcesSerializer(mapper)); this.serializers.put(HalLinkListSerializer.class, - new HalLinkListSerializer(curieProvider, mapper, accessor, halConfiguration)); + new HalLinkListSerializer(curieProvider, mapper, resolver, halConfiguration)); } /* diff --git a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsMediaTypeConfiguration.java b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsMediaTypeConfiguration.java index dc468a28..7aa41bc6 100644 --- a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsMediaTypeConfiguration.java +++ b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsMediaTypeConfiguration.java @@ -22,10 +22,10 @@ import java.util.List; import org.springframework.beans.factory.ObjectProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.support.MessageSourceAccessor; import org.springframework.hateoas.client.LinkDiscoverer; import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType; import org.springframework.hateoas.config.HypermediaMappingInformation; +import org.springframework.hateoas.mediatype.MessageResolver; import org.springframework.hateoas.mediatype.hal.CurieProvider; import org.springframework.hateoas.mediatype.hal.HalConfiguration; import org.springframework.hateoas.server.core.DelegatingLinkRelationProvider; @@ -48,7 +48,7 @@ class HalFormsMediaTypeConfiguration implements HypermediaMappingInformation { private final ObjectProvider curieProvider; private final ObjectProvider halFormsConfiguration; private final ObjectProvider halConfiguration; - private final MessageSourceAccessor messageSourceAccessor; + private final MessageResolver resolver; @Bean LinkDiscoverer halFormsLinkDiscoverer() { @@ -77,7 +77,7 @@ class HalFormsMediaTypeConfiguration implements HypermediaMappingInformation { mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.registerModule(new Jackson2HalFormsModule()); mapper.setHandlerInstantiator(new Jackson2HalFormsModule.HalFormsHandlerInstantiator(relProvider, - curieProvider.getIfAvailable(() -> CurieProvider.NONE), messageSourceAccessor, true, configuration)); + curieProvider.getIfAvailable(() -> CurieProvider.NONE), resolver, true, configuration)); return mapper; } diff --git a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplateBuilder.java b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplateBuilder.java index 8abe141f..7f47cc15 100644 --- a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplateBuilder.java +++ b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplateBuilder.java @@ -27,7 +27,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.springframework.context.MessageSourceResolvable; -import org.springframework.context.support.MessageSourceAccessor; import org.springframework.hateoas.Affordance; import org.springframework.hateoas.AffordanceModel.InputPayloadMetadata; import org.springframework.hateoas.AffordanceModel.PropertyMetadata; @@ -35,17 +34,17 @@ import org.springframework.hateoas.IanaLinkRelations; import org.springframework.hateoas.Link; import org.springframework.hateoas.MediaTypes; import org.springframework.hateoas.RepresentationModel; +import org.springframework.hateoas.mediatype.MessageResolver; import org.springframework.http.HttpMethod; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.util.Assert; -import org.springframework.util.StringUtils; @RequiredArgsConstructor class HalFormsTemplateBuilder { private final HalFormsConfiguration configuration; - private final MessageSourceAccessor accessor; + private final MessageResolver resolver; /** * Extract template details from a {@link RepresentationModel}'s {@link Affordance}s. @@ -96,8 +95,7 @@ class HalFormsTemplateBuilder { public HalFormsTemplate applyTo(HalFormsTemplate template, HalFormsTemplateBuilder.TemplateTitle templateTitle) { - return Optional.ofNullable(accessor.getMessage(templateTitle)) // - .filter(StringUtils::hasText) // + return Optional.ofNullable(resolver.resolve(templateTitle)) // .map(template::withTitle) // .orElse(template); } @@ -109,8 +107,12 @@ class HalFormsTemplateBuilder { private HalFormsProperty apply(HalFormsProperty property) { - String message = accessor.getMessage(PropertyPrompt.of(metadata, property)); - HalFormsProperty withPrompt = property.withPrompt(message); + String message = resolver.resolve(PropertyPrompt.of(metadata, property)); + + HalFormsProperty withPrompt = Optional.ofNullable(message) // + .map(it -> property.withPrompt(it)) // + .orElse(property); + HalFormsProperty withConfig = metadata.getPropertyMetadata(withPrompt.getName()) // .flatMap(it -> applyConfig(it, withPrompt)) // .orElse(withPrompt); diff --git a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/Jackson2HalFormsModule.java b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/Jackson2HalFormsModule.java index 279783d1..c9598a92 100644 --- a/src/main/java/org/springframework/hateoas/mediatype/hal/forms/Jackson2HalFormsModule.java +++ b/src/main/java/org/springframework/hateoas/mediatype/hal/forms/Jackson2HalFormsModule.java @@ -21,13 +21,13 @@ import java.util.HashMap; import java.util.Map; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; -import org.springframework.context.support.MessageSourceAccessor; import org.springframework.hateoas.CollectionModel; import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.Link; import org.springframework.hateoas.Links; import org.springframework.hateoas.PagedModel; import org.springframework.hateoas.RepresentationModel; +import org.springframework.hateoas.mediatype.MessageResolver; import org.springframework.hateoas.mediatype.hal.CurieProvider; import org.springframework.hateoas.mediatype.hal.Jackson2HalModule.EmbeddedMapper; import org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalHandlerInstantiator; @@ -163,7 +163,7 @@ class Jackson2HalFormsModule extends SimpleModule { private final Map, Object> serializers = new HashMap<>(); public HalFormsHandlerInstantiator(LinkRelationProvider resolver, CurieProvider curieProvider, - MessageSourceAccessor accessor, boolean enforceEmbeddedCollections, HalFormsConfiguration configuration) { + MessageResolver accessor, boolean enforceEmbeddedCollections, HalFormsConfiguration configuration) { super(resolver, curieProvider, accessor, enforceEmbeddedCollections, configuration.getHalConfiguration()); @@ -180,10 +180,9 @@ class Jackson2HalFormsModule extends SimpleModule { } public HalFormsHandlerInstantiator(LinkRelationProvider relProvider, CurieProvider curieProvider, - MessageSourceAccessor messageSource, boolean enforceEmbeddedCollections, - AutowireCapableBeanFactory beanFactory) { + MessageResolver resolver, boolean enforceEmbeddedCollections, AutowireCapableBeanFactory beanFactory) { - this(relProvider, curieProvider, messageSource, enforceEmbeddedCollections, + this(relProvider, curieProvider, resolver, enforceEmbeddedCollections, beanFactory.getBean(HalFormsConfiguration.class)); } diff --git a/src/test/java/org/springframework/hateoas/client/Server.java b/src/test/java/org/springframework/hateoas/client/Server.java index f4ffdb97..460405f8 100644 --- a/src/test/java/org/springframework/hateoas/client/Server.java +++ b/src/test/java/org/springframework/hateoas/client/Server.java @@ -15,10 +15,15 @@ */ package org.springframework.hateoas.client; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.springframework.context.MessageSource; -import org.springframework.context.support.MessageSourceAccessor; +import static net.jadler.Jadler.*; +import static org.hamcrest.Matchers.*; + +import java.io.Closeable; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.Collections; +import java.util.UUID; + import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.ResourceLoader; import org.springframework.hateoas.CollectionModel; @@ -26,6 +31,7 @@ import org.springframework.hateoas.EntityModel; import org.springframework.hateoas.Link; import org.springframework.hateoas.LinkRelation; import org.springframework.hateoas.MediaTypes; +import org.springframework.hateoas.mediatype.MessageResolver; import org.springframework.hateoas.mediatype.hal.CurieProvider; import org.springframework.hateoas.mediatype.hal.Jackson2HalModule; import org.springframework.hateoas.server.LinkRelationProvider; @@ -35,18 +41,8 @@ import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StreamUtils; -import java.io.Closeable; -import java.io.IOException; -import java.nio.charset.Charset; -import java.util.Collections; -import java.util.UUID; - -import static net.jadler.Jadler.closeJadler; -import static net.jadler.Jadler.initJadler; -import static net.jadler.Jadler.onRequest; -import static net.jadler.Jadler.port; -import static org.hamcrest.Matchers.equalTo; -import static org.mockito.Mockito.mock; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; /** * Helper class for integration tests. @@ -68,8 +64,8 @@ public class Server implements Closeable { this.mapper = new ObjectMapper(); this.mapper.registerModule(new Jackson2HalModule()); - this.mapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider, CurieProvider.NONE, - new MessageSourceAccessor(mock(MessageSource.class)))); + this.mapper.setHandlerInstantiator( + new Jackson2HalModule.HalHandlerInstantiator(relProvider, CurieProvider.NONE, MessageResolver.NONE)); initJadler() // .withDefaultResponseContentType(MediaTypes.HAL_JSON.toString()) // diff --git a/src/test/java/org/springframework/hateoas/mediatype/hal/Jackson2HalIntegrationTest.java b/src/test/java/org/springframework/hateoas/mediatype/hal/Jackson2HalIntegrationTest.java index 07cc9813..a89802e7 100755 --- a/src/test/java/org/springframework/hateoas/mediatype/hal/Jackson2HalIntegrationTest.java +++ b/src/test/java/org/springframework/hateoas/mediatype/hal/Jackson2HalIntegrationTest.java @@ -16,7 +16,6 @@ package org.springframework.hateoas.mediatype.hal; import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; import java.io.IOException; import java.util.ArrayList; @@ -34,12 +33,14 @@ import org.springframework.context.support.MessageSourceAccessor; import org.springframework.context.support.StaticMessageSource; import org.springframework.hateoas.*; import org.springframework.hateoas.PagedModel.PageMetadata; +import org.springframework.hateoas.mediatype.MessageResolver; import org.springframework.hateoas.mediatype.hal.HalConfiguration.RenderSingleLinks; import org.springframework.hateoas.mediatype.hal.Jackson2HalModule.HalHandlerInstantiator; import org.springframework.hateoas.server.LinkRelationProvider; import org.springframework.hateoas.server.core.AnnotationLinkRelationProvider; import org.springframework.hateoas.server.core.DelegatingLinkRelationProvider; import org.springframework.hateoas.server.core.EmbeddedWrappers; +import org.springframework.lang.Nullable; import com.fasterxml.jackson.databind.ObjectMapper; @@ -80,18 +81,15 @@ public class Jackson2HalIntegrationTest extends AbstractJackson2MarshallingInteg static final String SINGLE_WITH_ONE_EXTRA_ATTRIBUTES = "{\"_links\":{\"self\":{\"href\":\"localhost\",\"title\":\"the title\"}}}"; static final String SINGLE_WITH_ALL_EXTRA_ATTRIBUTES = "{\"_links\":{\"self\":{\"href\":\"localhost\",\"hreflang\":\"en\",\"title\":\"the title\",\"type\":\"the type\",\"deprecation\":\"/customers/deprecated\"}}}"; - MessageSource messageSource = mock(MessageSource.class); - @BeforeEach void setUpModule() { - MessageSourceAccessor accessor = new MessageSourceAccessor(messageSource); LinkRelationProvider provider = new DelegatingLinkRelationProvider(new AnnotationLinkRelationProvider(), DefaultLinkRelationProvider.INSTANCE); mapper.registerModule(new Jackson2HalModule()); mapper.setHandlerInstantiator( - new HalHandlerInstantiator(provider, CurieProvider.NONE, accessor, new HalConfiguration())); + new HalHandlerInstantiator(provider, CurieProvider.NONE, MessageResolver.NONE, new HalConfiguration())); } /** @@ -410,8 +408,7 @@ public class Jackson2HalIntegrationTest extends AbstractJackson2MarshallingInteg } }; - assertThat(getCuriedObjectMapper(provider, messageSource).writeValueAsString(resources)) - .isEqualTo(MULTIPLE_CURIES_DOCUMENT); + assertThat(getCuriedObjectMapper(provider).writeValueAsString(resources)).isEqualTo(MULTIPLE_CURIES_DOCUMENT); } /** @@ -450,8 +447,7 @@ public class Jackson2HalIntegrationTest extends AbstractJackson2MarshallingInteg void rendersSingleLinkAsArrayWhenConfigured() throws Exception { mapper.setHandlerInstantiator(new HalHandlerInstantiator(new AnnotationLinkRelationProvider(), CurieProvider.NONE, - new MessageSourceAccessor(messageSource), - new HalConfiguration().withRenderSingleLinks(RenderSingleLinks.AS_ARRAY))); + MessageResolver.NONE, new HalConfiguration().withRenderSingleLinks(RenderSingleLinks.AS_ARRAY))); RepresentationModel resourceSupport = new RepresentationModel<>(); resourceSupport.add(new Link("localhost").withSelfRel()); @@ -483,9 +479,8 @@ public class Jackson2HalIntegrationTest extends AbstractJackson2MarshallingInteg void rendersSpecificRelWithSingleLinkAsArrayIfConfigured() throws Exception { AnnotationLinkRelationProvider provider = new AnnotationLinkRelationProvider(); - MessageSourceAccessor accessor = new MessageSourceAccessor(messageSource); - mapper.setHandlerInstantiator(new HalHandlerInstantiator(provider, CurieProvider.NONE, accessor, + mapper.setHandlerInstantiator(new HalHandlerInstantiator(provider, CurieProvider.NONE, MessageResolver.NONE, new HalConfiguration().withRenderSingleLinksFor("foo", RenderSingleLinks.AS_ARRAY))); RepresentationModel resource = new RepresentationModel<>(); @@ -563,15 +558,15 @@ public class Jackson2HalIntegrationTest extends AbstractJackson2MarshallingInteg } private ObjectMapper getCuriedObjectMapper(CurieProvider provider) { - return getCuriedObjectMapper(provider, messageSource); + return getCuriedObjectMapper(provider, null); } - private ObjectMapper getCuriedObjectMapper(CurieProvider provider, MessageSource messageSource) { + private ObjectMapper getCuriedObjectMapper(CurieProvider provider, @Nullable MessageSource messageSource) { ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new Jackson2HalModule()); - mapper.setHandlerInstantiator(new HalHandlerInstantiator(new AnnotationLinkRelationProvider(), provider, - new MessageSourceAccessor(messageSource))); + mapper.setHandlerInstantiator( + new HalHandlerInstantiator(new AnnotationLinkRelationProvider(), provider, MessageResolver.of(messageSource))); return mapper; } diff --git a/src/test/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsMessageConverterUnitTest.java b/src/test/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsMessageConverterUnitTest.java index d186c6f1..7f2fca98 100644 --- a/src/test/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsMessageConverterUnitTest.java +++ b/src/test/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsMessageConverterUnitTest.java @@ -16,7 +16,6 @@ package org.springframework.hateoas.mediatype.hal.forms; import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -25,12 +24,11 @@ import java.io.OutputStream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.context.MessageSource; -import org.springframework.context.support.MessageSourceAccessor; import org.springframework.core.io.ClassPathResource; import org.springframework.hateoas.Link; import org.springframework.hateoas.MediaTypes; import org.springframework.hateoas.RepresentationModel; +import org.springframework.hateoas.mediatype.MessageResolver; import org.springframework.hateoas.mediatype.hal.CurieProvider; import org.springframework.hateoas.server.core.AnnotationLinkRelationProvider; import org.springframework.hateoas.server.mvc.TypeConstrainedMappingJackson2HttpMessageConverter; @@ -58,7 +56,7 @@ class HalFormsMessageConverterUnitTest { this.mapper.registerModule(new Jackson2HalFormsModule()); this.mapper.setHandlerInstantiator( new Jackson2HalFormsModule.HalFormsHandlerInstantiator(new AnnotationLinkRelationProvider(), CurieProvider.NONE, - new MessageSourceAccessor(mock(MessageSource.class)), true, new HalFormsConfiguration())); + MessageResolver.NONE, true, new HalFormsConfiguration())); TypeConstrainedMappingJackson2HttpMessageConverter converter = new TypeConstrainedMappingJackson2HttpMessageConverter( RepresentationModel.class); @@ -95,7 +93,8 @@ class HalFormsMessageConverterUnitTest { assertThat(halFormsDocument.getTemplates().keySet()).containsExactly("default"); assertThat(halFormsDocument.getTemplates().get("default").getContentType()).isEqualTo("application/hal+json"); assertThat(halFormsDocument.getTemplates().get("default").getHttpMethod()).isEqualTo(HttpMethod.GET); - assertThat(halFormsDocument.getTemplates().get("default").getMethod()).isEqualTo(HttpMethod.GET.toString().toLowerCase()); + assertThat(halFormsDocument.getTemplates().get("default").getMethod()) + .isEqualTo(HttpMethod.GET.toString().toLowerCase()); } @Test diff --git a/src/test/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplateBuilderUnitTest.java b/src/test/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplateBuilderUnitTest.java index 9d104e6f..e283bc2c 100644 --- a/src/test/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplateBuilderUnitTest.java +++ b/src/test/java/org/springframework/hateoas/mediatype/hal/forms/HalFormsTemplateBuilderUnitTest.java @@ -16,7 +16,6 @@ package org.springframework.hateoas.mediatype.hal.forms; import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; import lombok.Getter; @@ -28,10 +27,10 @@ import javax.validation.constraints.Pattern; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; -import org.springframework.context.support.MessageSourceAccessor; import org.springframework.hateoas.Link; import org.springframework.hateoas.RepresentationModel; import org.springframework.hateoas.mediatype.Affordances; +import org.springframework.hateoas.mediatype.MessageResolver; import org.springframework.http.HttpMethod; /** @@ -46,7 +45,7 @@ public class HalFormsTemplateBuilderUnitTest { HalFormsConfiguration configuration = new HalFormsConfiguration(); configuration.registerPattern(CreditCardNumber.class, "[0-9]{16}"); - HalFormsTemplateBuilder builder = new HalFormsTemplateBuilder(configuration, mock(MessageSourceAccessor.class)); + HalFormsTemplateBuilder builder = new HalFormsTemplateBuilder(configuration, MessageResolver.NONE); PatternExample resource = new PatternExample(); resource.add(Affordances.of(new Link("/examples")) // @@ -80,8 +79,7 @@ public class HalFormsTemplateBuilderUnitTest { .withName("post") // .toLink()); - HalFormsTemplateBuilder builder = new HalFormsTemplateBuilder(new HalFormsConfiguration(), - mock(MessageSourceAccessor.class)); + HalFormsTemplateBuilder builder = new HalFormsTemplateBuilder(new HalFormsConfiguration(), MessageResolver.NONE); Map templates = builder.findTemplates(model); diff --git a/src/test/java/org/springframework/hateoas/mediatype/hal/forms/Jackson2HalFormsIntegrationTest.java b/src/test/java/org/springframework/hateoas/mediatype/hal/forms/Jackson2HalFormsIntegrationTest.java index 42094558..5757fee7 100644 --- a/src/test/java/org/springframework/hateoas/mediatype/hal/forms/Jackson2HalFormsIntegrationTest.java +++ b/src/test/java/org/springframework/hateoas/mediatype/hal/forms/Jackson2HalFormsIntegrationTest.java @@ -16,7 +16,6 @@ package org.springframework.hateoas.mediatype.hal.forms; import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; import lombok.Getter; import net.minidev.json.JSONArray; @@ -38,7 +37,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; -import org.springframework.context.support.MessageSourceAccessor; import org.springframework.context.support.StaticMessageSource; import org.springframework.core.io.ClassPathResource; import org.springframework.hateoas.AbstractJackson2MarshallingIntegrationTest; @@ -51,6 +49,7 @@ import org.springframework.hateoas.PagedModel; import org.springframework.hateoas.RepresentationModel; import org.springframework.hateoas.UriTemplate; import org.springframework.hateoas.mediatype.Affordances; +import org.springframework.hateoas.mediatype.MessageResolver; import org.springframework.hateoas.mediatype.hal.CurieProvider; import org.springframework.hateoas.mediatype.hal.DefaultCurieProvider; import org.springframework.hateoas.mediatype.hal.Jackson2HalIntegrationTest; @@ -64,6 +63,7 @@ import org.springframework.hateoas.server.core.EmbeddedWrappers; import org.springframework.hateoas.support.EmployeeResource; import org.springframework.hateoas.support.MappingUtils; import org.springframework.http.HttpMethod; +import org.springframework.lang.Nullable; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.databind.ObjectMapper; @@ -82,7 +82,7 @@ class Jackson2HalFormsIntegrationTest extends AbstractJackson2MarshallingIntegra new Link("bar", IanaLinkRelations.PREV) // ); - MessageSource messageSource = mock(MessageSource.class); + // MessageSource messageSource = mock(MessageSource.class); @BeforeEach void setUpModule() { @@ -94,8 +94,7 @@ class Jackson2HalFormsIntegrationTest extends AbstractJackson2MarshallingIntegra mapper.registerModule(new Jackson2HalFormsModule()); mapper.setHandlerInstantiator(new HalFormsHandlerInstantiator( // - provider, CurieProvider.NONE, new MessageSourceAccessor(messageSource, Locale.US), true, - new HalFormsConfiguration())); + provider, CurieProvider.NONE, MessageResolver.NONE, true, new HalFormsConfiguration())); mapper.configure(SerializationFeature.INDENT_OUTPUT, true); } @@ -378,7 +377,7 @@ class Jackson2HalFormsIntegrationTest extends AbstractJackson2MarshallingIntegra } }; - assertThat(getCuriedObjectMapper(provider, messageSource).writeValueAsString(resources)) + assertThat(getCuriedObjectMapper(provider).writeValueAsString(resources)) .isEqualTo(MappingUtils.read(new ClassPathResource("multiple-curies-document.json", getClass()))); } @@ -587,18 +586,20 @@ class Jackson2HalFormsIntegrationTest extends AbstractJackson2MarshallingIntegra private ObjectMapper getCuriedObjectMapper() { - return getCuriedObjectMapper(new DefaultCurieProvider("foo", UriTemplate.of("http://localhost:8080/rels/{rel}")), - messageSource); + return getCuriedObjectMapper(new DefaultCurieProvider("foo", UriTemplate.of("http://localhost:8080/rels/{rel}"))); } - private ObjectMapper getCuriedObjectMapper(CurieProvider provider, MessageSource messageSource) { + private ObjectMapper getCuriedObjectMapper(CurieProvider provider) { + return getCuriedObjectMapper(provider, null); + } + + private ObjectMapper getCuriedObjectMapper(CurieProvider provider, @Nullable MessageSource messageSource) { ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new Jackson2HalFormsModule()); mapper.setHandlerInstantiator(new HalFormsHandlerInstantiator(new AnnotationLinkRelationProvider(), provider, - messageSource == null ? null : new MessageSourceAccessor(messageSource, Locale.US), true, - new HalFormsConfiguration())); + MessageResolver.of(messageSource), true, new HalFormsConfiguration())); mapper.configure(SerializationFeature.INDENT_OUTPUT, true); mapper.setSerializationInclusion(Include.NON_NULL); diff --git a/src/test/java/org/springframework/hateoas/mediatype/vnderror/VndErrorsMarshallingTest.java b/src/test/java/org/springframework/hateoas/mediatype/vnderror/VndErrorsMarshallingTest.java index f0e31707..0b72d051 100755 --- a/src/test/java/org/springframework/hateoas/mediatype/vnderror/VndErrorsMarshallingTest.java +++ b/src/test/java/org/springframework/hateoas/mediatype/vnderror/VndErrorsMarshallingTest.java @@ -16,7 +16,6 @@ package org.springframework.hateoas.mediatype.vnderror; import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; import java.io.FileInputStream; import java.io.IOException; @@ -26,10 +25,9 @@ import java.nio.charset.Charset; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.context.MessageSource; -import org.springframework.context.support.MessageSourceAccessor; import org.springframework.core.io.ClassPathResource; import org.springframework.hateoas.Link; +import org.springframework.hateoas.mediatype.MessageResolver; import org.springframework.hateoas.mediatype.hal.CurieProvider; import org.springframework.hateoas.mediatype.hal.Jackson2HalModule; import org.springframework.hateoas.mediatype.vnderrors.VndErrors; @@ -67,8 +65,8 @@ class VndErrorsMarshallingTest { jackson2Mapper = new com.fasterxml.jackson.databind.ObjectMapper(); jackson2Mapper.registerModule(new Jackson2HalModule()); - jackson2Mapper.setHandlerInstantiator(new Jackson2HalModule.HalHandlerInstantiator(relProvider, CurieProvider.NONE, - new MessageSourceAccessor(mock(MessageSource.class)))); + jackson2Mapper.setHandlerInstantiator( + new Jackson2HalModule.HalHandlerInstantiator(relProvider, CurieProvider.NONE, MessageResolver.NONE)); jackson2Mapper.configure(SerializationFeature.INDENT_OUTPUT, true); VndError error = new VndError("42", "Validation failed!", //