Spring Integration enriching payload using DSL - spring-integration

I am using Spring Integration to consume RSS feeds. Once I get a feed item, I need to enhance the data by using a field from the payload, call a Java class to get some additional data and store this with the payload before writing all the data to the DB.
What is the best way to do this, a payload enricher or a service activator and how to specify this using DSL?
Finally as the payload is an SyndEntry object, do I need to create a new payload with new fields?
Any pointers would be helpful.

Yes, you need a new payload type; you can use a simple POJO...
#Bean
public Enricher enricher() {
return new Enricher();
}
public static class Enricher {
public Enhanced enhance(SyndEntry entry) {
return new Enhanced(entry, "foo", "bar");
}
}
Then, in the DSL...
...
.handle("enricher", "enhance")
...

Related

Spring Integration Jms InboundGateway Dynamic Reply Queue

Is it possible to have a dynamic reply queue with Jms OutboundGateway via DSL?
Jms.inboundGateway(jmsListenerContainer)
.defaultReplyQueueName("queue1 or queue2")
Working Solution using ThreadLocal and DestinationResolver:
private static final ThreadLocal<String> REPLY_QUEUE = new ThreadLocal<>();
IntegrationFlows.from(Jms.inboundGateway(listenerContainer)
.defaultReplyQueueName("defaultQueue1")
.destinationResolver(destinationResolver())
.transform(p -> {
// on some condition, else "defaultQueue1"
REPLY_QUEUE.set("changedToQueue2");
return p;
})
#Bean
public DestinationResolver destinationResolver() {
return (session, destinationName, pubSubDomain) -> session.createQueue(REPLY_QUEUE.get());
}
It is not clear from where you'd like to take that dynamic reply queue name, but there is another option:
/**
* #param destinationResolver the destinationResolver.
* #return the spec.
* #see ChannelPublishingJmsMessageListener#setDestinationResolver(DestinationResolver)
*/
public S destinationResolver(DestinationResolver destinationResolver) {
By default this one is a DynamicDestinationResolver which does only this: return session.createQueue(queueName);. Probably here you can play somehow with your different names to determine.
Another way is to have a JMSReplyTo property set in the request message from the publisher.
UPDATE
Since you cannot rely on a default Reply-To JMS message property, I suggest you to look into a ThreadLocal in your downstream flow where you can place your custom header. Then a custom DestinationResolver can take a look into that ThreadLocal variable for a name to delegate to the same mentioned DynamicDestinationResolver.

Spring Integration: Switch routing dynamically

A spring integration based converter consumes the messages from one system, checks, converts and sends it to the other one.
Should the target system be down, we stop the inbound adapters, but would also like to persist locally or forward the currently "in-flight" converted messages. For that would simply like to reroute the messages from the normal output channel to some "backup"-channel dynamically.
In the docs I have found only the option to route the messages based on their headers ( so on some step before in flow I would have to add those dynamically once the targer system is not availbale), or based on the payload type, which is not really my case. The case with adding dynamically some header, and then filtering it out down the pipe, or during de-/serializing still seems not the best approach for me. I would like rather to be able to turn a switch(on some internal Event) that would then reroute those "in-flight" messages to the "backup"-channel.
What would be a best SI approach to achive this? Thanks!
The router could not only be based on the the payload type or some header. You really can have a general POJO method invocation to return a channel, its name or some routing key which is mapped. That POJO method indeed can check some internal system state and produce this or that routing key.
So, you may have something like this in the router configuration:
.route(myRouter())
where your myRouter is something like this:
#Bean
MyRouter myRouter() {
return;
}
and its internal code might be like this:
public class MyRouter {
#Autowired
private SystemState systemState;
String route(Object payload) {
return this.systemState.isActive() ? "successChannel" : "backupChannel";
}
}
The same can be achieved a simple lambda definition:
.<Object, Boolean>route(p -> systemState().isActive(),
m -> m.channelMapping(true, "sucessChannel")
.channelMapping(false, "backupChannel"))
Also...
private final AtomicBoolean switcher = new AtomicBoolean();
#Bean
public IntegrationFlow flow() {
return IntegrationFlows.from(() -> "foo", e -> e.poller(Pollers.fixedDelay(Duration.ofSeconds(5))))
.route(s -> switcher.get() ? "foo" : "bar")
.get();
}

GlobalChannelInterceptor pass array of patterns

I am Spring Integration 4.3.13 and trying to pass patterns when configuring #GlobalChannelInterceptor
Here is the example
#Configuration
public class IntegrationConfig{
#Bean
#GlobalChannelInterceptor(patterns = "${spring.channel.interceptor.patterns:*}")
public ChannelInterceptor channelInterceptor(){
return new ChannelInterceptorImpl();
}
}
properties file has following values:
spring.channel.interceptor.patterns=*intchannel, *event
I am using direct channels with names that end with these two string
springintchannel
registrationevent
With the above config, both the channels should have interceptor configured but it is not getting configured.
The comma-separate value isn't support there currently.
I agree that we need to fix it, so feel free to raise a JIRA on the matter and we will file a solution from some other place.
Meanwhile you can do this as a workaround:
#Bean
public GlobalChannelInterceptorWrapper channelInterceptorWrapper(#Value("${spring.channel.interceptor.patterns:*}") String[] patterns) {
GlobalChannelInterceptorWrapper globalChannelInterceptorWrapper = new GlobalChannelInterceptorWrapper(channelInterceptor());
globalChannelInterceptorWrapper.setPatterns(patterns);
return globalChannelInterceptorWrapper;
}

ServiceStack - Request Binding JSON encoded parameter

I have an existing application that sends a Request with a parameter named 'filters'. The 'filters' parameter contains a string that is JSON encoded. Example:
[{"dataIndex":"fieldName", "value":"fieldValue"}, {"dataIndex":"field2", "value":"value2"}].
Using ServiceStack, I would like to bind this as a property on a C# object (class Grid). Is there a preferred method to handle this? Here are the options I can think of. I don't think either 'feel' correct.
Option 1:
I do have a 'ServiceModel' project and this would create a dependency on it which I don't really like.
In AppHost.Configure() method add
RequestBinders[typeof(Grid)] => httpReq => {
return new Grid() {
Filters = new ServiceStack.Text.JsonSerializer<IList<Filter>>().DeserializeFromString(httpReq.QueryString["filters"])
}
}
Option 2:
Seems kind of 'hacky'
public class Grid
{
private string _filters;
public dynamic Filters {
get
{
ServiceStack.Text.JsonSerializer<IList<Filter().DeserializeFromString(_filters);
}
set
{
_filters = value;
}
}
}
You can send Complex objects in ServiceStack using the JSV Format.
If you want to send JSON via the QueryString you can access it from inside your Service of Request filters with something like:
public object Any(Request req) {
var filters = base.Request.QueryString["Filters"].FromJson<List<Filter>>();
}
Note: Interfaces on DTOs are bad practice.

Jax-rs client marshal list of entities using jax-b

I need to marshal a list of entities using jax-b in a jax-rs client, without creating a wrapper class for each entity needed (there are manny entities!). I notice the service is able to marshal a list of customers like this:
<customers>
<customer>.....</customer>
<customer>.....</customer>
</customers>
Which I on the client side is able to unmarshal by finding all customer nodes and adding them to a list manually. (I guess there's a better way to do this?)
Now, the real problem here is when I want to send a list of an entity(eg. customers) to the service. I want to marshal this list into an xml string before writing this string as the payload of the request to the service. This does not work since java.util.List or its descendants is not known to the marshaller.
javax.xml.bind.Marshaller.marshal(list, StringWriter);
javax.xml.bind.Unmarshaller.unmarshal(org.​w3c.​dom.node)
Any help is much appreciated!
Thanks!
-Runar
Edit:
Here's a snippet from the customer class:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Customer implements Serializable {
private String name;
.....
}
I'm trying to write a lightweight client using no 3rd party libraries not part of the standard implementation. Because of this I have written my own httpclient taking in payload objects, marhalling them and passing them to the payload of the request. When the response is received I read the xml and send it to unmarshalling. It would be awsome if I could do the marshalling/unmarshalling directly to/from string just as my jax-rs service does.
Ok, so I found no good solution to this. But since it seems the pattern for my jax-rs service is to generate a root node called <class name " + "s>, I did this to be able to send lists of objects to the service:
if (obj instanceof List) {
List list = (List) obj;
if (!list.isEmpty()) {
// Since the jax-b marshaller does not allow to send lists directly,
// we must attempt to create the xml as the jax-rs service would expect them,
// wrapped with the classname pluss an s.
String wrapper = list.get(0).getClass().getSimpleName() + "s";
// Make first letter in class-name lower-case
wrapper = Character.toLowerCase(wrapper.charAt(0)) +
(wrapper.length() > 1 ? wrapper.substring(1) : "");
marshaller.setProperty(javax.xml.bind.Marshaller.JAXB_FRAGMENT, true);
writer.write("<" + wrapper + ">");
for (Object o : (List) obj) {
marshaller.marshal(o, writer);
}
writer.write("</" + wrapper + ">");
}
} else {
marshaller.marshal(obj, writer);
}

Resources