Content resolution example with Spring Cloud Stream
The Spring documentation mentions one of the core features of the framework:
It tries to automatically convert incoming message payloads to type
Person
with the following code:
@Bean public Consumer<Person> log() {
return person -> {
System.out.println("Received: " + person);
};
}
My attempts to have the incoming message automatically convert into a Person
type with Solace have failed.
Please point me to an Solace example of sending & receiving messages thru Spring Cloud Stream where the incoming message payload is automatically converted.
Best Answer
-
I cloned your code, even installed a JDK21. Got it working by moving the Person event class out of the Application class and into it's own class.
Application.java
@SpringBootApplication public class HelloWorldSpringCloudStreamApplication { public static void main(String... args) { SpringApplication.run(HelloWorldSpringCloudStreamApplication.class, args); } @Bean public Consumer<PersonEvent> exampleEventConsumer() { return personEvent -> { System.out.println("Received event: " + personEvent.getName()); }; } }
PersonEvent.java
@JsonInclude(JsonInclude.Include.NON_NULL) public class PersonEvent { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "PersonEvent, name="+name; } }
Also, you were escaping your quotes in your payload..? Here's a handy command-line tool my colleague wrote that is useful for things like this:
Just download the latest release, that's what I use:$ ./stm send -t try-me -m '{"name":"Aaron"}'
Hope that's enough to get you going now..! 👍🏼
3
Answers
-
Spring Cloud Stream documentation … https://docs.spring.io/spring-cloud-stream/docs/current/reference/html/spring-cloud-stream.html#spring-cloud-stream-preface-adding-message-handler
0 -
This is also referenced in the Solace code labs …
By default when coding your Spring Cloud Stream microservice you are writing Spring Cloud Function beans that can be re-used for multiple purposes and can leverage the framework's
Content Type Negotiation
to pass your POJOs directly into the function while decoupling your business logic from the specific runtime target and triggering mechanism (web endpoint, stream processor, task).
https://codelabs.solace.dev/codelabs/spring-cloud-stream-beyond/?_gl=1*155nf2j*_ga*MTMyOTkzNDg4OC4xNzExOTM3MDcw*_ga_XZ3NWMM83E*MTcxMzEwMTgxOC4xMy4wLjE3MTMxMDE4MTguMC4wLjA.#3
0 -
Hi @dakotahnorth, welcome to the Community. You're going to need some way to deserialize the payload into your POJO. What format is your payload in? Spring can't just guess how to do this.
For example, if you look at the code generated by the Spring Cloud Stream generator for AsyncAPI docs, it uses Jackson
@JsonInclude
to build the POJOs from JSON schemas:package com.example.app; import com.fasterxml.jackson.annotation.JsonInclude; @JsonInclude(JsonInclude.Include.NON_NULL) public class PassengerInfo { public PassengerInfo () { } public PassengerInfo ( String passengerAction, String destination, java.math.BigDecimal passengerId, String tripId, String vipStatus) { this.passengerAction = passengerAction; this.destination = destination; this.passengerId = passengerId; this.tripId = tripId; this.vipStatus = vipStatus; } private String passengerAction; private String destination; private java.math.BigDecimal passengerId; private String tripId; private String vipStatus; public String getPassengerAction() { return passengerAction; } public PassengerInfo setPassengerAction(String passengerAction) { this.passengerAction = passengerAction; return this; } public String getDestination() { return destination; } public PassengerInfo setDestination(String destination) { this.destination = destination; return this; } public java.math.BigDecimal getPassengerId() { return passengerId; } public PassengerInfo setPassengerId(java.math.BigDecimal passengerId) { this.passengerId = passengerId; return this; } public String getTripId() { return tripId; } public PassengerInfo setTripId(String tripId) { this.tripId = tripId; return this; } public String getVipStatus() { return vipStatus; } public PassengerInfo setVipStatus(String vipStatus) { this.vipStatus = vipStatus; return this; } public String toString() { return "PassengerInfo [" + " passengerAction: " + passengerAction + " destination: " + destination + " passengerId: " + passengerId + " tripId: " + tripId + " vipStatus: " + vipStatus + " ]"; } }
0 -
Well … from the Solace AWS Broker "Try Me!" page I tried sending both binary and text messages.
The payload in the message text box was{\"name\":\"Miles Archer\"}
Changed my simple
PersonEvent
to be@JsonInclude(JsonInclude.Include.NON_NULL) public class PersonEvent { @JsonProperty("name") private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public PersonEvent(String name) { this.name = name; } public PersonEvent() { }
Still … I am not able to deserialize the message.
So was hoping there was some lab or github examples with Solace that you can point me to that has this all working with both publishing and receiving messages.0 -
Posted the following to Github as an example …
0 -
I cloned your code, even installed a JDK21. Got it working by moving the Person event class out of the Application class and into it's own class.
Application.java
@SpringBootApplication public class HelloWorldSpringCloudStreamApplication { public static void main(String... args) { SpringApplication.run(HelloWorldSpringCloudStreamApplication.class, args); } @Bean public Consumer<PersonEvent> exampleEventConsumer() { return personEvent -> { System.out.println("Received event: " + personEvent.getName()); }; } }
PersonEvent.java
@JsonInclude(JsonInclude.Include.NON_NULL) public class PersonEvent { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "PersonEvent, name="+name; } }
Also, you were escaping your quotes in your payload..? Here's a handy command-line tool my colleague wrote that is useful for things like this:
Just download the latest release, that's what I use:$ ./stm send -t try-me -m '{"name":"Aaron"}'
Hope that's enough to get you going now..! 👍🏼
3 -
@dakotahnorth, as you see in the Spring Cloud Stream documentation the framework automatically provides this deserialization for you as long as it can understand the payload.
We have examples of it in our samples repo here:
- see the cloud-stream-sink for the simplest of examples:https://github.com/SolaceSamples/solace-samples-spring/tree/master/cloud-stream-sink
Note that the framework provides these MessageConverters and if you need others you can see the "User-defined Message Converters" section right after that one.
FYI @Aaron no need to do the serialization and deserialization yourself!
1 -
Urgh … so embarrassing! Since the documentation had the Person class as part of the same class, I just figured it was the "correct" way.
Thank you for point that out, as well as, the quotes!SUPER, SUPER helpful!
1 -
Thanks Marc!
The sink example is helpful … and for "the simplest of examples", would be great to see this be even simpler!
Instead of
channel.send(MessageBuilder.withPayload(new SensorReading("test", 50, BaseUnit.FAHRENHEIT)).build());
For example … with the help from above, this is what I am now doing with a
StreamBridge
streamBridge.send("try-me", new PersonPOJO("Miles Archer"));
1 -
Re the
Person
code above …@JsonInclude(JsonInclude.Include.NON_NULL)
Isn't needed. Works with just a POJO with no special annotations.
1 -
Awesome @dakotahnorth, glad you figured it out :)
When you commented this which sample are you referring to? It's been a bit since I looked through these samples, but usually we use MessageBuilder when we are trying to show adding message headers.
The sink example is helpful … and for "the simplest of examples", would be great to see this be even simpler!
0