Is it possible to customize serialization used by the Spring Cache abstraction? - java

I have a Java web service that uses Redis for caching. Initially I created a CacheService that directly accessed the Redisson client in order to handle caching. I recently refactored the cache handling to use the Spring Cache abstraction, which made the code a lot cleaner and encouraged modular design. Unfortunately Spring uses Jackson to serialize/deserialize the cached objects, resulting in the cached values being much larger than before due to type info being stored in the JSON. This caused an unacceptable increase in response time in reads from the cache. Is there any way to customize the way that Spring serializes and deserializes the cached content? I'd like to replace it with my own logic, but don't see anything in the docs. I'd rather not have to roll my own AspectJ cache implementation if possible.

The RedisCacheManager takes a RedisOperations and you can configure there how serialization works. You can tune serialization for keys and values though I suspect key should use StringRedisSerializer.

Redisson also provides Spring Cache integration. It supports many popular codecs: Jackson JSON, Avro, Smile, CBOR, MsgPack, Kryo, FST, LZ4, Snappy and JDK Serialization.
Here is an example:
#Bean
CacheManager cacheManager(RedissonClient redissonClient) {
Codec codec = new JsonJacksonCodec();
Map<String, CacheConfig> config = new HashMap<String, CacheConfig>();
config.put("testMap", new CacheConfig(24*60*1000, 12*60*1000));
return new RedissonSpringCacheManager(redissonClient, config, codec);
}

Related

Spring boot #Cacheble with Ehcache

I'm using Spring boot with Ehcache for caching some data in the application.
The application is a rest service that caches some data that has high usage.
The code in our controllers looks like:
#Cacheable("CategoryModels")
#GetMapping("/category/{companyId}")
public List<CategoryViewModel> getAllCategories(#PathVariable(value = "companyId", required = true) long companyId,
#RequestHeader("user") String user) {
//custom code here
}
Now in some situations the users are getting different data sets back from the server. Can someone explain this in the above situation?
If data is changed in the database I refresh the cache and the program will auto update the updated data to the
For refreshing the cache I use a custom written method:
Cache categoryCache = (Cache) manager.getCache("CategoryModels").getNativeCache();
categoryCache.removeAll();
categoryController.getAllCategories(company.getCompanyId(), null);
I have the same behavior on other caches that are used and refreshed on the same way the above cache is used.
You should try to parametrize your cache definition with :
#Cacheable(value="CategoryModels", key="{ #root.methodName, #companyId, #user.id }")
It may be a couple of things. First off the default key resolver that spring provides does not consider anything but the names of the parameters. The cleanest way to fix this kid to write your own key revolver that considers both class and method, without this it could be possible to get back data from a completely different method that happens to share the same parameter list.

Spring Kafka JsonSerializer usage

I am trying to follow the instructions here:
http://docs.spring.io/spring-kafka/docs/1.1.1.RELEASE/reference/htmlsingle/#_serialization_deserialization_and_message_conversion
To set up a KafkaTemplate that can serialize and send some simple Java POJOs that I have. But I found the documentation vague and confusing, especially this part:
For this purpose Spring for Apache Kafka also provides
JsonSerializer/JsonDeserializer implementations based on the Jackson
JSON processor. When JsonSerializer is pretty simple and just lets to
write any Java object as a JSON byte[]
...
Although
Serializer/Deserializer API is pretty simple and flexible from the
low-level Kafka Consumer and Producer perspective, it is not enough on
the Messaging level, where KafkaTemplate and #KafkaListener are
present.
...
The MessageConverter can be
injected into KafkaTemplate instance directly and via
AbstractKafkaListenerContainerFactory bean definition for the
#KafkaListener.containerFactory() property
So my question is:
What is the type of my KafkaTemplate? Is it KafkaTemplate<String, Object>? Or is it KafkaTemplate<String, String>?
What is my
Serializer class? Is it StringSerializer, or is it JsonSerializer?
Do
I use kafkaTemplate.setMessageConverter(new StringJsonMessageConverter()) when creating my KafkaTemplate bean?
Apologies if these are stupid questions - I'm trying to understand the correct way of setting it up rather than "hacking it till it kinda works".
<String, Object>
JsonSerializer
The message converter is only used when using the send that takes a Message<?> and with a JsonSerializer you should use the default one.

Guava cache as time-based cleanup storage in Spring ap

I have a Spring Boot/MVC app that should store some Simple POJOs sent from users for 15 minutes of time. When this time expires, this object should be removed from ConcurrentHashMap. At first ConcurrentHashMap was something I wanted to implement this feature with, but then I thought maybe leveraging Guava's cache would be a better option, since it has a time-based eviction out of the box.
My service implementation
#CachePut(cacheNames = "teamConfigs", key = "#authAccessDto.accessToken")
#Override
public OAuthAccessDto saveConfig(OAuthAccessDto authAccessDto) {
return authAccessDto;
}
#Override
#Cacheable(cacheNames = "teamConfigs")
public OAuthAccessDto getConfig(String accessToken) {
// we assume that the cache is already populated
return null;
}
As you can see, we save data with saveConfig and then when we need to retrieve it, we call getConfig.
Cache configuration in Spring boot is the following (yml file):
spring:
cache:
cache-names: teamConfigs
guava:
spec: expireAfterWrites=900s
However, after reading Guava's cache doc https://github.com/google/guava/wiki/CachesExplained I found that Guava can clean up caches even before the time defined in expireAfterWrites elapses (and even before it runs out of memory).
How can I configure Guava Cache to keep objects until the time expires (considering it did not run out of memory). Maybe I should opt for some other solution?
I don't know about Guava but you could use any JSR-107 compliant provider and a simple configuration that would look like this:
#Bean
public JCacheManagerCustomizer cacheManagerCustomizer() {
return cm -> {
cm.createCache("teamConfigs", new MutableConfiguration<>()
.setExpiryPolicyFactory(CreatedExpiryPolicy
.factoryOf(new Duration(MINUTES, 15)));
};
}
Caffeine (a rewrite of Guava with Java8) has a JSR-107 provider so you could use that. Maybe that version does not exhibit what you experience with Guava? If so, support is expected in Spring Boot 1.4 but you can give the JSR-107 support a try right now.
If you don't want to use that, you could give expiringmap a try. It does not have any Cache implementation in the Spring abstraction but since it's a Map you can easily wrap it, something like on the top of my head:
#Bean
public Cache teamConfigsCache() {
Map<Object, Object> map = ExpiringMap.builder()
.expiration(15, TimeUnit.MINUTES)
.build();
return new ConcurrentMapCache("teamConfigs", map , true);
}
If Spring Boot discovers at least a Cache bean in your configuration, it auto-creates a CacheManager implementation that wraps them. You can force that behaviour via the spring.cache.type property.

REST API invocation in Java

Suppose I need to write a Java client, which calls a REST API (with HTTP GET). I know it returns the data in JSON by default and I do not need to supply any headers.
Now I can use either Apache HttpClient to invoke the API or read the URL directly (get a stream from the URL with url.openStream and read the data). The second approach seems to me much simpler. Which one would you suggest and why ?
All the REST clients provide a wrapper over basic java URL based APIs. These clients are easy to use and provide all the necessary functionality. Your code will be much cleaner in case you use Apache HttpClient. And Apache's API are quite reliable.
I would use special libraries for that, like Jersey client or Apache CXF client.
https://jersey.java.net/documentation/latest/client.html
http://cxf.apache.org/docs/jax-rs.html
These ones are part of Java EE standard, a well defined specification which is widely used.
For JSON, consider https://github.com/FasterXML/jackson. Depending on what client you use, you will find information about how to make it work.
If you are not a big fan of JavaEE, and you look for neat and elegant API, and you are interested in working with a language on top of Java, Groovy HTTPBuilder is such a library that works like a charm!
twitter = new RESTClient( 'https://twitter.com/statuses/' )
resp = twitter.post( path : 'update.xml',
body : [ status:msg, source:'httpbuilder' ],
requestContentType : URLENC )
assert resp.status == 200
assert resp.data.user.screen_name == userName
You can use spring-data-rest and Spring's RestTemplate. No need to write a webapp as you can bootstrap Spring easily into a standalone java application putting AnnotationConfigApplicationContext in the Main(). It's quite simple.
For example, suppose you have a Restful URL, http://localhost:8080/croot/books/ that returns a list of books (deserialized into objects of type Book).
Using Spring's RestTemplate you can do the following:
public Resource<List<Resource<Book>>> findAll() {
return restTemplate
.exchange(
"http://localhost:8080/croot/books/",
HttpMethod.GET,
null,
new ParameterizedTypeReference<Resource<List<Resource<Book>>>>() {
}).getBody();
}
You can also process this using spring-data-hateoas allowing you to further decouple the client from the server and helps process what to do next, say in pagination.
This is a very simplified/contrived example but the REST support in Spring 3 combined with the spring-data framework is quite elegant.
Using Spring you also get the advantage of Jackson for JSON processing as the RestTemplate will have one of the flavors of Jackson's message converters (provided through MappingJackson2HttpMessageConverter for example) in it's list of default converters used for processing.

Spring DataBinder equivalent for Json

I am currently working on a web project which is using Play Framework 2.1.0. Play supports a decent API for parsing form data and mapping that to the Model beans directly. Which looks something like,
Form<Employee> form = Form.form(Employee.class).bindFromRequest();
if (form.hasErrors()) {
return badRequest(template.render(form));
}
This API also does validations on the fly and is capable of handling binding failures, when say a String could not be converted to an Integer. The Form API keeps the collection of errors mapped to the name of the property. Underlying the Form API, Play is using DataBinder of Spring's validation framework which is actually doing all the magic.
I was wondering if there is similar binding API to convert from JSON to the bean directly, with support for handling binding failures?
Play 2.0 uses Jackson internally which fails when there are binding failures and simply throws an exception. I looked at the code and does not look easy to supress these errors.
Is there some framework that can satisfy my requirement out of the box?
Essentially, I need the framework to convert from JSON to Java Bean, which can handle binding failures gracefully.
It would be wonderful if it allows me to collect them somewhere so I can generate appropriate validation errors. I will run custom validations on the parsed object using javax.validation APIs to perform more specific validations once the JSON is parsed into the Bean.
I achieved this by adding custom deserializers in Jackson
SimpleDeserializers deserializers = new SimpleDeserializers();
deserializers.addDeserializer(Integer.class, new MyIntegerDeserializer(null));
deserializers.addDeserializer(Long.class, new MyLongDeserializer(null));
ObjectMapper mapper = new ObjectMapper().setDeserializerProvider(
new StdDeserializerProvider().withAdditionalDeserializers(deserializers));
MyModel value = mapper.treeToValue(node, MyModel.class);
MyIntegerDeserializer and MyLongDeserializer are custom deserializers for Integer and Long values respectively. These are in my case exact copy of the internal default corresponding deserializer classes with additional code to gracefully handle NumberFormatException

Categories

Resources