Spring Kafka JsonSerializer usage - java

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.

Related

Customise Spring Boot Serialisation process with custom jackson ObjectMapper

A while ago I asked this question in Stackoverflow which helped me be build this spring boot app
At the moment I have created my own object mapper with a SimpleModule.
Then using my object mapper I am serializing the Node object inside the Controller class and I am returning a ResponseEntity<String> based on the depth value that is passed in the request parameter.
What I want to achieve is to let Spring Boot do the serialization as it happens normaly. So from inside my Controller I want to return ResponseEntity<Node> and somehow replace Spring Boots ObjectMapper with mine and do the serialization using the depth request parameter.
Any ideas are more than welcomed.
Thanks in advance

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

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);
}

Camel: Bean Proxy to CXF Endpoint

I'm currently trying to get familiar with Servicemix, Camel, CXF, etc. and have basically the same question as somebody had four years ago here:
How do I convert my BeanInvocation object in camel to a message body and headers?
Unfortunately, the answer there don't help me much. As one of the answers mentions: all examples on the Camel website concern themselves with sending something to a bean from CXF.
I have a bean proxy endpoint that I'm using in a POJO, injected via
#Produce(uri ="direct:start")
MyService producer; //public interface example.MyService { void myMethod(MyObject o);}
When I use another bean endpoint at the other end, implementing a consumer for that interface, this all works fine. What I now would like to do is to use camel-cxf to consume a web service implementing that interface instead. I created a cxfEndpoint via:
<cxf:cxfEndpoint id="cxfEndpoint"
address="http://localhost:8080/MyService/services/MyService"
wsdlURL="http://localhost:8080/MyService/services/MyService?wsdl"
serviceName="s:MyService"
serviceClass="example.MyService"
endpointName="s:MyService"
xmlns:s="http://example" />
What I'm now basically trying to do is, in a RouteBuilder:
from( "direct:start" ).to( "cxf:bean:cxfEndpoint" );
but get an Exception, when trying invoke something on the proxy object:
Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Part
{http://example}o should be of type example.MyObject, not
org.apache.camel.component.bean.BeanInvocation
From what I understand, the Spring proxy object generates a BeanInvocation object that can be consumed by another bean endpoint, and I have to transform this into a way the cxf can generate a SOAP request out of it (or is there some automatic conversion?).
But I'm kind of stuck doing that:
I tried soap marshalling as described at http://camel.apache.org/soap.html or writing my own Processor, but I'm not even sure if I just failed, or if that's not how it's supposed to work. I also tried to set the cxfEndpoint into the different message modes without success.
Any pointers what I should be generally doing would be greatly appreciated!
So after a week of trial and error, I found that the answer is quite simple. If the cxfEndpoint is set to POJO mode (the default), the solution is to just grab the invocation parameters and stuff them into the message body instead:
from( "direct:start" ).process( new Processor() {
#Override
public void process( Exchange e) throws Exception {
final BeanInvocation bi = e.getIn().getBody( BeanInvocation.class );
e.getIn().setBody( bi.getArgs() );
}
} ).to( "cxf:bean:cxfEndpoint" )
I guess this could be done more elegantly somehow though.

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

Apache Camel: Convert JSON to a POJO using Camel methods

I have a REST server which sends JSON in response body. I have recently started reading about Apache Camel. I use following to send requests to my REST service.
from("direct:start").setHeader("token", simple("234da"))
.to("http://localhost:8088/foo/bar/?foo1=bar1");
Now the response will be a JSON, is there any way I get this JSON directly into a POJO using some method ahead of to() (something like this)?
to("http://localhost:8088/foo/bar/?foo1=bar1").toPOJO();
I would prefer a non Spring solution.
Thanks
Little details from my side - although late
Create jsonFormatter and then unmarshal with class you need
JsonDataFormat jsonDataFormat = new JsonDataFormat(JsonLibrary.Jackson);
this can be used in marshalling
from("direct:consume-rest")
.log("calling bean method...")
.to("http://localhost:8080/greeting?name=baba")
//.process(svProcessor) // any extra process if you want
.unmarshal().json(JsonLibrary.Jackson, Greeting.class)
.bean(GreetingHelper.class, "print")
.log("converted to bean ...")
.end()
;
Helper class method
public void print (#Body Greeting greeting) {
Apache Camel provides a component to marshal and unmarshal POJO to and from JSON.
In your case, it would be :
from("direct:start").setHeader("token", simple("234da"))
.to("http://localhost:8088/foo/bar/?foo1=bar1")
.unmarshal().json();
By the way, you may need to configure your json library to do it and I suggest you take look at the official configuration.

Categories

Resources