org.springframework.hateoas.config.HateoasConfiguration Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spring-hateoas Show documentation
Show all versions of spring-hateoas Show documentation
Library to support implementing representations for
hyper-text driven REST web services.
/*
* Copyright 2015-2024 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.config;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import java.util.stream.Collectors;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.support.AbstractMessageSource;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.core.io.Resource;
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;
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.OrderAwarePluginRegistry;
import org.springframework.plugin.core.PluginRegistry;
import org.springframework.plugin.core.config.EnablePluginRegistries;
import org.springframework.plugin.core.support.PluginRegistryFactoryBean;
import org.springframework.util.ClassUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Common HATEOAS specific configuration.
*
* @author Oliver Gierke
* @author Greg Turnquist
* @soundtrack Elephants Crossing - Wait (Live at Stadtfest Dresden)
* @since 0.19
*/
@Configuration(proxyBeanMethods = false)
@EnablePluginRegistries({ LinkDiscoverer.class })
public class HateoasConfiguration {
static String I18N_BASE_NAME = "rest-messages";
static String I18N_DEFAULTS_BASE_NAME = "rest-default-messages";
private @Autowired ApplicationContext context;
@Bean
public MessageResolver messageResolver() {
return MessageResolver.of(lookupMessageSource());
}
@Bean
WebConverters hypermediaWebMvcConverters(ObjectProvider mapper,
List information, Optional comparator) {
comparator.ifPresent(information::sort);
return WebConverters.of(mapper.getIfUnique(ObjectMapper::new), information);
}
// RelProvider
@Bean
LinkRelationProvider defaultRelProvider() {
return ClassUtils.isPresent("org.atteo.evo.inflector.English", null) //
? new EvoInflectorLinkRelationProvider()
: new DefaultLinkRelationProvider();
}
@Bean
AnnotationLinkRelationProvider annotationRelProvider() {
return new AnnotationLinkRelationProvider();
}
@Primary
@Bean
DelegatingLinkRelationProvider _relProvider(
PluginRegistry relProviderPluginRegistry) {
return new DelegatingLinkRelationProvider(relProviderPluginRegistry);
}
@Bean
OrderAwarePluginRegistry relProviderPluginRegistry(ApplicationContext context) {
PluginRegistryFactoryBean factory = new PluginRegistryFactoryBean<>();
factory.setApplicationContext(context);
factory.setType(LinkRelationProvider.class);
factory.setExclusions(new Class[] { DelegatingLinkRelationProvider.class });
factory.afterPropertiesSet();
return factory.getObject();
}
// LinkDiscoverers
@Bean
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 final AbstractMessageSource lookupMessageSource() {
List candidates = loadResourceBundleResources(I18N_DEFAULTS_BASE_NAME, false);
if (candidates.isEmpty() && loadResourceBundleResources(I18N_BASE_NAME, true).isEmpty()) {
return null;
}
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setResourceLoader(context);
messageSource.setBasename("classpath:".concat(I18N_BASE_NAME));
messageSource.setDefaultEncoding(StandardCharsets.UTF_8.toString());
if (!candidates.isEmpty()) {
messageSource.setCommonMessages(loadProperties(candidates));
}
return messageSource;
}
@Nullable
private final Properties loadProperties(List sources) {
PropertiesFactoryBean factory = new PropertiesFactoryBean();
factory.setLocations(sources.toArray(new Resource[sources.size()]));
try {
factory.afterPropertiesSet();
return factory.getObject();
} catch (IOException o_O) {
throw new IllegalStateException("Could not load default properties from resources!", o_O);
}
}
private final List loadResourceBundleResources(String baseName, boolean withWildcard) {
try {
return Arrays //
.stream(context.getResources(String.format("classpath:%s%s.properties", baseName, withWildcard ? "*" : ""))) //
.filter(Resource::exists) //
.collect(Collectors.toList());
} catch (IOException e) {
return Collections.emptyList();
}
}
}