Extending RepositoryRestMvcConfiguration breaks Jackson LocalDateTime Serialisation - java

I'm attempting to extend RepositoryRestMvcConfiguration to override the uriToEntityConverter for a custom one, however doing that then causes Jackson to fail to be able to serialise/deserialise LocalDateTime from String. The code below shows my subclass.
I've also tried adding jackson-datatype-jsr310 into the pom to see if I can force support, but without my subclass it works anyway so that was mostly a dead-end.
#Configuration
#Import(RepositoryRestMvcConfiguration.class)
public class RepositoryRestMvcConfigurer extends RepositoryRestMvcConfiguration {
#Override
#ConfigurationProperties(prefix = "spring.data.rest")
public RepositoryRestConfiguration config() {
return super.config();
}
#Override
protected UriToEntityConverter uriToEntityConverter(ConversionService conversionService) {
return new OverriddenUriToEntityConverter(persistentEntities(), repositoryInvokerFactory(conversionService), repositories());
}
}
EDIT:
I've solved the problem by annotating my LocalDateTime properties to specify what serialiser and deserialiser to use, but that feels more like a voodoo ritual than a solution.

So I ended up self-solving this. To actually extended that you also need:
A class that extends RepositoryRestConfigurerAdapter
A class that returns that previous class and also implements the annotations for AutoConfigureAfter
I based mine on the RepositoryRestMvcAutoConfiguration class actually within Spring but changed it from being #ConditionalOnMissingBean(RepositoryRestMvcConfiguration.class) to #ConditionalOnBean(ExtendedRepositoryRestMvcConfiguration.class). That allowed it to properly configure the RestRepository with my overridden functions.

Related

SpringBoot: how to enable a #Configuration Class only if the corresponding #EnablerAnnotation has been used on a class?

How can I enable a #Configuration class only if the corresponding #EnableCustomConfiguration annotation has been used?
To make it clear I am trying to recreate the behaviour of common SpringBoot annotations, like e.g. #EnableEurekaClient, #EnableWebSecurity and so on.
This is my enabler:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface EnableMultitenancy {}
Here my configuration properties:
#Getter
#Setter
#Validated
#ConfigurationProperties("multitenancy")
public class MultitenancyProperties {
#NotEmpty(message = "You must provide at least one tenant")
private List<Tenant> tenants;
}
and this is my Configuration class:
#Configuration
#Conditional("On EnableMultitenancy used")
#EnableConfigurationProperties(MultitenancyProperties.class)
public class MultitenancyConfiguration{
#Bean
public MyFirstBean first(MultitenancyProperties properties){
return new MyFirstBean(properties);
}
#Bean
public MySecondBean second(MultitenancyProperties properties){
return new MySecondBean(properties);
}
}
How can I write such a condition, e.g. the annotation has been used on a class/component?
org.springframework.context.annotation.ConditionContext interface shows theability of #Conditional.
ConditionContext contains five methods:
getRegistry
getBeanFactory
getEnvironment
getResourceLoader
getClassLoader
So it seems your cannot directly depend on annotation, eg: #EnableCaching. But you can depend on the bean related to them, eg: ProxyCachingConfiguration for EnableCaching.
But i think is not a good design. Usually we will declare our dependency, this makes component sperately, also make things easy. When user want to use our component, they don't need to add all of depended annotation, eg: #EnableEureka,#EnableWebSecurity, just # MultitenancyConfiguration.
In your MultitenancyConfiguration class, you just need to replace the current #Conditional annotation with #ConditionalOnClass(EnableMulitenancy.class).
Update: Sorry, I misunderstood. Why don't you just import the MultitenancyConfiguration class by using #Import(MultitenancyConfiguration.class) in your EnableMultitenancy class? This will guarantee that your MultitenancyConfiguration class is enabled whenever #EnableMultitenancy is used.

Spring+Jackson: How to set visibility of a class without using #JsonAutoDetect annotation

In my spring-boot application, I have a global configuration on Jackson's ObjectMapper which told Jackson not to serialize object by fields but getters:
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder()
{
return new Jackson2ObjectMapperBuilder()
{
#Override
public void configure(ObjectMapper objectMapper)
{
super.configure(objectMapper);
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.NONE);
objectMapper.setVisibility(PropertyAccessor.GETTER, Visibility.PUBLIC_ONLY);
objectMapper.setVisibility(PropertyAccessor.IS_GETTER, Visibility.PUBLIC_ONLY);
}
};
}
However, now I'm dealing with a special case. I need to serialize a class which is not written by myself (a class form dependnecy library). Since the class does not declared getters, Jackson will ignore those fields.
Here's how the external class look like:
public class DirectionsResult
{
public GeocodedWaypoint geocodedWaypoints[];
public DirectionsRoute routes[];
}
Although using #JsonAutoDetect annotation can customerize a class's visibility for Jackson, this does not work with external classes.
So how can I set visibility of a class without using #JsonAutoDetect annotation and also not to change the global configuration?
You should be able to use jacksons MixIn feature. With this approach you can control all the configuration of a class by another class definition of your choice.
https://github.com/FasterXML/jackson-docs/wiki/JacksonMixInAnnotations
You can also check out one of my github projects to see the use of that feature:
https://github.com/Antibrumm/jackson-antpathfilter

Caching superclass methods with Spring 3.2 and EHCache

I have the following scenario:
Spring 3.2
EHCache
A superclass that can not be modified (inside a jar), with a structure similar to this:
public abstract class SuperClass<E extends Object> implements SuperIface<E> {
public void insert(E entity) {
}
}
A subclass, (can be modified), with this structure and a little more complex condition in #CacheEvict
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
#Service
#CacheEvict(value = "entityCache", allEntries = true, condition = "'insert'.equals(#root.methodName)")
public class SubClass extends SuperClass<Entity> implements ISubIface {
public void anotherMethod() {
}
}
Is there any option to say Spring to make effective #CacheEvict annotation when insert method of SuperClass is invoked? In other words, Can I remove cache entries when insert method is invoked from a SubClass instance?
Note that I'm looking for the way to do it via configuration. I know that I can override superclass' methods in subclass, but for some circumstances (another logic in superclass) I prefer not to do that.
You can create cache advices on any class you want using xml configuration if you can't change the source.
Having said that, your example does not make much sense to me: the class is abstract so you actually need an implementation to invoke that method. Are you saying that you have multiple implementations and you want all these implementations to have a CacheEvict behaviour? If you only have one, I don't see the problem of having an override that merely call super + the annotation.

Spring cache abstraction (AdviceMode.ASPECTJ) not working inside spring-data-jpa repositories

i'm using spring-data-jpa 1.9.0.RELEASE and want to use the spring caching mechanism inside my repositories, e.g.
public interface LandDao extends CrudRepository<Land, Long> {
#Cacheable("laender")
Land findByName(String land)
}
Here is my cache configuration:
#Configuration
#EnableCaching(mode=AdviceMode.ASPECTJ)
public class EhCacheConfiguration extends CachingConfigurerSupport {
...
Note that i'm using AdviceMode.ASPECTJ (compile time weaving). Unfortunately caching is not working when calling the repo method 'findByName'.
Changing the caching mode to AdviceMode.PROXY all works fine.
To ensure that caching works in principle with aspectJ, i wrote the following service:
#Service
public class LandService {
#Autowired
LandDao landDao;
#Cacheable("landCache")
public Land getLand(String bez) {
return landDao.findByName(bez);
}
}
In this case the cache works like a charm. So i think that all parts of my application are correctly configured and the problem is the combination of spring-data-jpa and AspectJ caching mode. Does anyone have an idea what's going wrong here?
Okay, found the answer to my question by myself. The javadoc of the responsible aspect org.springframework.cache.aspectj.AnnotationCacheAspect says:
When using this aspect, you must annotate the implementation class (and/or methods within that class), not the interface (if any) that the class implements. AspectJ follows Java's rule that annotations on interfaces are not inherited.
So it's not possible to use the #Cacheable annotation inside repository interfaces together with aspectj. My solution now is to make use of custom implementations for Spring Data repositories:
Interface for custom repository functionality:
public interface LandRepositoryCustom {
Land findByNameCached(String land);
}
Implementation of custom repository functionality using query dsl:
#Repository
public class LandRepositoryImpl extends QueryDslRepositorySupport
implements LandRepositoryCustom {
#Override
#Cacheable("landCache")
public Land findByNameCached(String land) {
return from(QLand.land).where(QLand.land.name.eq(land)).singleResult(QLand.land);
}
}
Note the #Cacheable annotation for the findByNameCached method.
Basic repository interface:
public interface LandRepository extends CrudRepository<Land, Long>, LandRepositoryCustom {
}
Using the repository:
public class SomeService {
#Autowired
LandRepository landDao;
public void foo() {
// Cache is working here:-)
Land land = landDao.findByNameCached("Germany");
}
}
It would be helpful to add a note relating to this limitation in the spring data reference.

Jackson: ignore the #JsonIgnoreProperties annotation

I have a class that I serialize and I use the #JsonIgnoreProperties at the class level to exclude some fields from it.
Lately I have an use case where I need those fields serialized.
Is there a way to make a writer/reader that ignores the annotation?
I was looking into #JsonView but it seems #JsonIgnoreProperties takes precedence over it.
#JsonFilter could help you in this case.
By defining custom json filter, Jackson will dynamically resolve filter given class uses, dynamically, allowing per-call reconfiguration of filtering.
You can find detailed explanation and usage example here
Some usefull information about dynamic ignoral you find here
I have come up with a solution, don't know if it the best one but gets the job done ...
I ended up using #JsonView.
So I have 2 views like this:
public class Views {
public static class Public { }
public static class Extended extends Public { }
}
and the default Spring mapper configured as
mapper.setConfig(mapper.getSerializationConfig().withView(Views.Public.class));
mapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);
and the class looks like
public class Foo{
String name;
#JsonView(Views.Extended.class)
String title;
...}
By setting up a default view on the object mapper it causes it to ignore all the other not specified views. The fields with no annotation will always be serialized, as per config.
Then, when I need the whole class to be serialized I use:
objectMapper.writer().withView(Views.Extended.class).writeValueAsString(value);

Categories

Resources