I have found a problem caused by extended RepositoryRestMvcConfiguration to add custom functionality, like custom validators. I'm using Spring-Boot 1.2.1.
When you extend RepositoryRestMvcConfiguration properties like spring.data.rest.baseUri do not work because the @ConfigurationProperties(prefix = "spring.data.rest") is defined in RepositoryRestMvcBootConfiguration. (the Spring-Boot specific Rest configuration class).
This has the same outward appearance as https://github.com/spring-projects/spring-boot/issues/1171, but a different root cause.
Here is an example configuration class:
@Configuration
@Slf4j
public class RestConfiguration extends RepositoryRestMvcConfiguration {
@Autowired
BeforeCreateUserValidator beforeCreateUserValidator;
@Override
protected void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
validatingListener.addValidator("beforeCreate", beforeCreateUserValidator);
}
}
Normally, I would extend RepositoryRestMvcBootConfiguration instead of RepositoryRestMvcConfiguration to solve this problem, but because it has default permissions it isn't visible unless I put my class in the same package.
For now I've worked around this by copying the code from RepositoryRestMvcBootConfiguration into my class, and adding the following to my app class:
@EnableAutoConfiguration(exclude= {RepositoryRestMvcAutoConfiguration.class})
I'm not sure if this is a documentation bug, or what. Most documentation online seems to tell you to extend RepositoryRestMvcConfiguration to get custom behavior, but this seems to cause problems.
_Note_: I had a bunch of updates to this issue as I was debugging, it turns out I had a bug, so I've clarified this bug to just the relevant points now.
The thing is RepositoryRestMvcConfiguration exposes a RepositoryRestConfiguration which is the thing that we override to expose the spring.data.rest properties.
You should be able to override that class and still use the auto-configuration. The main difference really is that this class is in Spring Data Rest and not in Spring Boot so when you extend the one from SD Rest you basically wipe out all our customizations.
You should extend from RepositoryRestMvcBootConfiguration instead. We should make that class public and explain in the documentation how to use it.
For now you can easily workaround the problem by adding the following code to your RestConfiguration
@Bean
@ConfigurationProperties(prefix = "spring.data.rest")
@Override
public RepositoryRestConfiguration config() {
return super.config();
}
You should extend from RepositoryRestMvcBootConfiguration instead. We should make that class public and explain in the documentation how to use it.
Yes, I agree that would be the best solution, thanks!
Just landed here with the exact same problem, mine was setup as YAML but stopped as soon as I extended RepositoryRestMvcConfiguration to add some config stuff.
thanks for the "public RepositoryRestConfiguration config()" workaround - solved the issue for me
@Configuration
public class DataRepoConfiguration extends RepositoryRestMvcConfiguration {
@Override
@ConfigurationProperties(prefix = "spring.data.rest")
public RepositoryRestConfiguration config() {
return super.config();
}
@Override
protected void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
//TODO
}
}
good, but confused : isnt that exactly what snicoll commented on 22 Jan 2015? Can you explain what is new?
ä½ èƒ½è§£é‡Šä»€ä¹ˆæ˜¯æ–°çš„ï¼Ÿ
Note that this is broken after the work to fix this: #6581
The recommended approach is now to implement a RepositoryRestConfigurer bean.
Hi all, I used the workaround suggested from @snicoll but with Spring Boot 2 things changed. I don't understand how to make it work. Could someone suggest the hint so it will remain in this ticket for everyone with the same problem?
@drenda that issue has been fixed over 3 years ago so there is no need to use the workaround anymore.
@snicoll, thanks for your reply. I overriden RepositoryRestMvcConfiguration in this way because I need to change setFallbackToSystemLocale().
@Configuration
public class CustomRepositoryRestMvcConfiguration extends RepositoryRestMvcConfiguration {
public CustomRepositoryRestMvcConfiguration(ApplicationContext context, ObjectFactory<ConversionService> conversionService) {
super(context, conversionService);
}
public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_DATE_TIME;
@Override
@Bean
public MessageSourceAccessor resourceDescriptionMessageSourceAccessor() {
try {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("rest-messages.properties"));
propertiesFactoryBean.afterPropertiesSet();
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:rest-messages");
messageSource.setCommonMessages(propertiesFactoryBean.getObject());
messageSource.setDefaultEncoding("UTF-8");
messageSource.setFallbackToSystemLocale(false);
return new MessageSourceAccessor(messageSource);
} catch (Exception o_O) {
throw new BeanCreationException("resourceDescriptionMessageSourceAccessor", "", o_O);
}
}
}
Unfortunately if I do this, my spring.data.rest.base-path=/api/v1 doesn't work anymore and everythin is mapped to /.
@drenda When you subclass RepositoryRestMvcConfiguration, that's a signal that you want to take complete control of Spring Data REST's configuration. As a result, the code that applies any spring.data.rest properties backs off. You can reinstate that logic by injecting RepositoryRestProperties and calling its applyTo method. Typically, that would be done in the configureRepositoryRestConfiguration method of a RepositoryRestConfigurerAdapter subclass. Take a look at Boot's SpringBootRepositoryRestConfigurer for inspiration.
Also, please open an issue against Spring Data REST to explore making it easier to disable falling back to the system locale. I'd normally suggest using a BeanPostProcessor but you can't get the MessageSource out of the MessageSourceAccessor without resorting to reflection.
@wilkinsona Thanks for your hint. I had that suspect. I tried to follow your suggestiong but injecting RepositoryRestProperties doesn't work:
Field properties in it.rebus.server.config.CustomRepositoryRestMvcConfiguration required a bean of type 'org.springframework.boot.autoconfigure.data.rest.RepositoryRestProperties' that could not be found.
Do you have some hint about that? For sure I'll open an issue and I'll post here the reference. Thanks
Those properties are normally enabled by RepositoryRestMvcAutoConfiguration but it's backed off in this case. You'll need to enable them:
@EnableConfigurationProperties(RepositoryRestProperties.class)
@wilkinsona THanks again. Now it is injected correctly. I opened a ticket in Spring Data Rest: https://jira.spring.io/browse/DATAREST-1228
My class now looks like this:
@Configuration
@EnableConfigurationProperties(RepositoryRestProperties.class)
public class CustomRepositoryRestMvcConfiguration extends RepositoryRestMvcConfiguration {
public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_DATE_TIME;
@Autowired
private RepositoryRestProperties properties;
@Autowired
private RepositoryRestConfiguration repositoryRestConfiguration;
public CustomRepositoryRestMvcConfiguration(ApplicationContext context, ObjectFactory<ConversionService> conversionService) {
super(context, conversionService);
}
@PostConstruct
public void postConstruct() {
this.properties.applyTo(repositoryRestConfiguration);
}
@Override
@Bean
public MessageSourceAccessor resourceDescriptionMessageSourceAccessor() {
try {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
propertiesFactoryBean.setLocation(new ClassPathResource("rest-messages.properties"));
propertiesFactoryBean.afterPropertiesSet();
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:rest-messages");
messageSource.setCommonMessages(propertiesFactoryBean.getObject());
messageSource.setDefaultEncoding("UTF-8");
messageSource.setFallbackToSystemLocale(false);
return new MessageSourceAccessor(messageSource);
} catch (Exception o_O) {
throw new BeanCreationException("resourceDescriptionMessageSourceAccessor", "", o_O);
}
}
}
I get a circular references error either trying to inject RepositoryRestConfiguration either calling super.repositoryRestConfiguration().
Probably I'm doing something wrong; on the other hand I can't build a bean like the one built in RepositoryRestMvcConfiguration because the implementation depends on some things I can't access from my extended class.
@Bean
public RepositoryRestConfiguration repositoryRestConfiguration() {
ProjectionDefinitionConfiguration configuration = new ProjectionDefinitionConfiguration();
// Register projections found in packages
for (Class<?> projection : getProjections(repositories())) {
configuration.addProjection(projection);
}
RepositoryRestConfiguration config = new RepositoryRestConfiguration(configuration, metadataConfiguration(),
enumTranslator());
configurerDelegate.configureRepositoryRestConfiguration(config);
return config;
}
@drenda Sorry, but I don't really understand what your problem is from those two snippets. GitHub issues aren't really the right place for this sort of thing. Can you please ask a question on Stack Overflow providing a minimal, complete, and verifiable example?