unable to set correlation id from a pojo - spring-integration

I am able to set correlation id using <int:correlation-id /> tag but I want to set it from a POJO. So I tried
<int:header-enricher id="he" input-channel="in" output-channel="test">
<int:correlation-id ref="heSimple" method="resolve" />
</int:header-enricher>
this doesnt works. CorrelationId is not modified. However if i change it to
<int:header-enricher id="he" input-channel="in" output-channel="test">
<int:header name="foo" ref="heSimple" method="resolve" />
</int:header-enricher>
this works fine. So basically I can modify any other header key other than correlation id from this pojo. I also tried
<int:header-enricher id="he" input-channel="in" output-channel="test">
<int:header name="correlationId" ref="heSimple" method="resolve" />
</int:header-enricher>
Interestingly that doesnt works either.
EDIT
private MessageChannel in;
in.send(MessageBuilder.withPayload("a1").build());
in.send(MessageBuilder.withPayload("b1").setCorrelationId("1").build());
somehow the problem seems to be related to setting the correlation id explicitly while sending the message. So in this case a1 message does ends up using the header enricher to set the new corelation id but the message b2 continue to use 1 as the correlation id.
Looking at debug logs it seems like a1 message is sent to transformer and invokes the header enricher but for message b2 the header enricher is not invoked
[11:44:23:052] [main] DEBUG o.s.i.t.MessageTransformingHandler - org.springframework.integration.transformer.MessageTransformingHandler#0 received message: [Payload=a1][Headers={timestamp=1399045463050, id=d20d68e1-ea7a-df20-4faf-d1864855851c}]
in hesimple for [Payload=a1][Headers={timestamp=1399045463050, id=d20d68e1-ea7a-df20-4faf-d1864855851c}]
[11:44:23:055] [main] DEBUG o.s.i.t.MessageTransformingHandler - handler 'org.springframework.integration.transformer.MessageTransformingHandler#0' sending reply Message: [Payload=a1][Headers={timestamp=1399045463055, id=3b72891c-aaa5-3570-7715-e020529b5f83, correlationId=test}]
for message b1 header enricher is skipped
[11:44:23:056] [main] DEBUG o.s.i.t.MessageTransformingHandler - org.springframework.integration.transformer.MessageTransformingHandler#0 received message: [Payload=b1][Headers={timestamp=1399045463055, id=9aa2bb7e-9c6c-66e5-0d64-3afea4a57ee4, correlationId=1}]
[11:44:23:056] [main] DEBUG o.s.i.t.MessageTransformingHandler - handler 'org.springframework.integration.transformer.MessageTransformingHandler#0' sending reply Message: [Payload=b1][Headers={timestamp=1399045463056, id=c8d54223-e506-727d-6bff-703755017403, correlationId=1}]
there is not much in the header enricher
public static class HESimple {
public String resolve(Message<String> message){
System.out.println("in hesimple for " + message);
return "test";
}
}

I just tested the first case and it works just fine for me.
What version of Spring Integration are you using?
What is the signature of the method?
There is nothing "special" about that header that would cause the different behavior you describe. I suggest you turn on DEBUG logging to figure out what's happening.
EDIT:
In order to overwrite existing values, you need to set overwrite="true".

Related

Message Template receive gives Dispatcher has no subscribers for channel

This is my spring-integration inbound and out bound which gets a list from a end point.
<http:inbound-gateway id="webListGateway"
request-channel="fromWeb_List"
reply-channel="toWeb_List"
path="/api/profile/V1/get"
supported-methods="GET">
<http:header name="container" expression="#pathVariables.container"/>
<http:header name="groupName" expression="#pathVariables.groupName"/>
<http:header name="userId" expression="#pathVariables.userId"/>
</http:inbound-gateway>
<int:header-enricher input-channel="fromWeb_List" output-channel="toCloud_List">
<int:header name="apikey" value=“1234”/>
</int:header-enricher>
<http:outbound-gateway id="profileListGateway"
request-channel="toCloud_List"
reply-channel="sync_preferences"
url=“localhost:8081/containers/{container}/groups/{groupName}/values/hierarchy/{userId}"
http-method="GET"
expected-response-type="java.lang.String"
charset="UTF-8"
extract-request-payload="false"
header-mapper="headerMapper"
encode-uri="true" >
<http:uri-variable name="container" expression="headers.container"/>
<http:uri-variable name="groupName" expression="headers.groupName"/>
<http:uri-variable name="userId" expression="headers.userId"/>
</http:outbound-gateway>
This is my recipient-list-router which send backs the list to requestor and also saves the list in another end point.
<int:recipient-list-router id="syncRouter" input-channel="sync_preferences">
<int:recipient channel="toWeb_List"/>
<int:recipient channel="toCloud_Save"/>
</int:recipient-list-router>
I am also trying to call the outbound gateway from java code and trying to get the response from toWeb_List channel by using receive method on MessageTemplate, which is giving me error
MessagingTemplate template = new MessagingTemplate();
Message<String> message1 = MessageBuilder.withPayload("")
.setHeader("container", “fwd”)
.setHeader("groupName", “foo”)
.setHeader("userId", “user”)
.build();
template.send((MessageChannel) CONTEXT.getBean("fromWeb_List"),message1);
PreList pre = (PreList) template.receive((MessageChannel)CONTEXT.getBean("toWeb_List"));
error
Dispatcher has no subscribers for channel 'application:springboot.toWeb_List'
Any Idea what I am doing wrong here.
You can't use DirectChannel for the MessagingGateway.receive():
protected final Message<?> doReceive(MessageChannel channel, long timeout) {
Assert.notNull(channel, "MessageChannel is required");
Assert.state(channel instanceof PollableChannel, "A PollableChannel is required to receive messages");
Another issue that reply-channel="toWeb_List" has to be registered as correlator in the Inbound Gateway to be able to receive messages, as Gary pointed. And that is done on demand, on the first request. That's why you get
that Dispatcher has no subscribers.
And really, please, try to explain what you would like to do.
UPDATE
If you are going to reuse that <int:recipient-list-router> from the HTTP Inbound and from some other similar request-reply place, you should consider to drop off usage of the reply-channel and just rely on the replyChannel in headers.
I mean there can be that toWeb_List channel bean definition, but should not use it from the reply-channel. In this case your config should be like this:
<int:recipient-list-router id="syncRouter" input-channel="sync_preferences">
<int:recipient channel="toWeb_List"/>
<int:recipient channel="toCloud_Save"/>
</int:recipient-list-router>
<int:bridge input-channel="toWeb_List"/>
The bridge is such a components to shift message from input channel to the output one, if present. Otherwise it consults MessageHeaders for the replyChannel value. And this is is populated exactly via those Inbound request-reply components such as <http:inbound-gateway> or plain <int:gateway> when you call directly from Java.
See more information in the Reference Manual.

Spring Integration JDBC inbound channel adapter not updating records

The application context of my Spring Boot application is:
<context:component-scan
base-package="org.mycompany.myproject.polling" />
<int:channel id="fromdb" />
<jdbc:embedded-database id="dataSource" type="H2" />
<int:service-activator input-channel="fromdb" ref="jdbcMessageHandler" />
<int-jdbc:inbound-channel-adapter
channel="fromdb" data-source="dataSource"
query="select * from Books where status = 0"
update="update Books set status = 1">
<int:poller fixed-delay="1000"/>
</int-jdbc:inbound-channel-adapter>
I have schema.sql and data.sql in the resources directory that create the table and insert data on startup with all records in the status column having value 0. The update query of the inbound channel adapter does not run, since I see that status column in H2 still has value 0.
What did I miss?
Your application works for me well after this simple modifications:
#SpringBootApplication
#ImportResource("application-context.xml")
public class DbpollerApplication {
public static void main(String[] args) {
SpringApplication.run(DbpollerApplication.class, args);
}
}
As you see I removed suspicious code for the ClassPathXmlApplicationContext and really let Spring Boot to load everything, including Embedded DataSource.
In the application-context.xml I removed <context:component-scan> and <jdbc:embedded-database> just because they are supplied by the Spring Boot per se.
After starting application I see in logs:
Row
column: ITEM_ID value: Book_id99
column: DESCRIPTION value: Book_description99
column: STATUS value: 0
Row
column: ITEM_ID value: XXX
column: DESCRIPTION value: last book
column: STATUS value: 0
And also I've copied from there and URL to the DB:
2018-02-21 16:49:40.357 INFO 10576 --- [ main] o.s.j.d.e.EmbeddedDatabaseFactory : Starting embedded database: url='jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa'
The part jdbc:h2:mem:testdb is just enough.
Then I've opened H2 Web console on the localhost:8080/h2-console, connected to the mentioned URL and did a SELECT from the BOOKS table and got this result:
Am I missing anything?

Unable to get Aggregator to work

I am trying to understand the Aggregator basics. Below is the use case I am trying to implement:
1) Read message (order details) from queue.
<?xml version="1.0" encoding="UTF-8"?>
<order xmlns="http://www.example.org/orders">
<orderItem>
<isbn>12333454443</isbn>
<quantity>4</quantity>
</orderItem>
<orderItem>
<isbn>545656777</isbn>
<quantity>50</quantity>
</orderItem>
..
..
</order>
One order message will contain multiple orderItem. And we can expect hundreds of order messages in the queue.
2) End Result ::
a) Each orderitem should be written to a file.
b) 4 such files should be written to a unique folder.
To give an example, lets say we got two order messages - each containing three orderitem.
So we need to create 2 folders :
In "folder 1", there should be 4 files(1 orderitem in each file)
In "folder 2", there should be 2 files(1 orderitem in each file). Here for simplicity we assume no more order messages came and we can write after 5 mins.
Implementation:
I am able to read the message from the queue (websphere MQ) and unmarshall the message successfully.
Used splitter to split the message based on orderitem count.
Used Aggregator to group the message in size of 4.
I unable to get the aggregator to work as per my understanding.
I push one order when 4 orderitem, the message is getting aggregated correctly.
I push one order with 5 orderitem, the first 4 is getting aggregated but the last one is sent to discard channel. This is expected as the MessageGroup is released so the last message is discarded.
I push two orders each containing 2 orderitem. The last 2 orderitem are sent to discard channel.
The correlation strategy is hardcoded (OrderAggregator.java) but the above case should have worked.
Need pointers on how to implement this use case where I can group them in 4 and write to unique folders.
Please note that the orderitem are all independent book orders and have no relation amongst them.
Below is the configuration.
spring-bean.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans">
<int:channel id="mqInbound"/>
<int:channel id="item"/>
<int:channel id="itemList"/>
<int:channel id="aggregatorDiscardChannel"/>
<int-jms:message-driven-channel-adapter id="jmsIn"
channel="mqInbound"
destination="requestQueue"
message- converter="orderMessageConverter"/>
<int:splitter input-channel="mqInbound" output-channel="item" expression="payload.orderItem"/>
<int:chain id="aggregateList" input-channel="item" output-channel="itemList" >
<int:header-enricher>
<int:header name="sequenceSize" expression="4" overwrite="true"/>
</int:header-enricher>
<int:aggregator correlation-strategy="orderAggregator" correlation-strategy-method="groupOrders" discard-channel="aggregatorDiscardChannel" />
</int:chain>
<int:service-activator input-channel="itemList" ref="displayAggregatedList" method="display"/>
<int:service-activator input-channel="aggregatorDiscardChannel" ref="displayAggregatedList" method="displayDiscarded"/>
<bean id="orderAggregator" class="com.samples.Aggregator.OrderAggregator"/>
<bean id="displayAggregatedList" class="com.samples.Aggregator.DisplayAggregatedList"/>
...
....
</beans>
OrderAggregator.java
public class OrderAggregator {
#Aggregator
public List<OrderItemType> sendList(List<OrderItemType> orderItemTypeList) {
return orderItemTypeList;
}
#CorrelationStrategy
public String groupOrders( OrderItemType orderItemType) {
return "items";
}
}
DisplayAggregatedList.java
public class DisplayAggregatedList {
public void display(List <OrderItemType> orderItemTypeList) {
System.out.println("######## Display Aggregated ##############");
for(OrderItemType oit : orderItemTypeList) {
System.out.println("### Isbn :" + oit.getIsbn() + ":: Quantity :" + oit.getQuantity());
}
}
public void displayDiscarded(Message<?> message) {
System.out.println("######## Display Discarded ##############" + message);
}
}
What you need is called expire-groups-upon-completion:
When set to true (default false), completed groups are removed from the message store, allowing subsequent messages with the same correlation to form a new group. The default behavior is to send messages with the same correlation as a completed group to the discard-channel.
If you need to release uncompleted groups anyway (2 orders left, for example), consider to use group-timeout: http://docs.spring.io/spring-integration/reference/html/messaging-routing-chapter.html#agg-and-group-to
Please, use expire-groups-upon-completion="true" and consider to use MessageCountReleaseStrategy` for release-strategy – Artem Bilan

Why aren't nLog custom event properties rendering through an email target?

I have a facade I'm using to wrap up my nLog calls. I'm using nLog's event properties to add a custom "longMessage" field. Here is the code:
public void Fatal(string shortMessage, string longMessage, Exception exception = null)
{
Logger _logger = LogManager.GetCurrentClassLogger();
LogEventInfo theEvent = new LogEventInfo(LogLevel.Debug, "", "Pass my custom value");
theEvent.Message = shortMessage;
theEvent.Properties["LongMessage"] = longMessage;
theEvent.Exception = exception;
_logger.Fatal(theEvent);
}
When I write nLog events to a file or to the database target, the LongMessage field renders correctly. But when I write to email, I get an empty string where my long message should be. Any ideas why outputting properties would work in a database target but not work in an email target?
<target name="themail" xsi:type="Mail"
smtpServer="VALID.INTERNAL.IP"
from="sender#address.com"
to="recip#address.com"
subject="${event-properties:item=LongMessage}"
body="${all-event-properties:format=[key]=[value]:separator=,:includeCallerInformation=true}"
html="true"
encoding="UTF-8">
I get the email just fine. It comes in great. But the subject and body are just blank. I've added other things to the subject and body to make sure I'm getting something in the subject and body of the email, and that works too. Everything works except the event-properties lookup -- and that only doesn't work on the email target.
I think you need to escape the equal sign in format - that's what I finally figured out:
${all-event-properties:format=[key]\=[value]:separator=,:includeCallerInformation=true}"

spring-integration is losing headers when sent to a subscriber

I am using spring-integration with hornetQ. The problem is that I have put a custom header in the message (Method), but when it hits the subscriber the header is no longer available. I there some sort of configuration property I need to setup to preserve headers?
An application receives the message (I can see the Method header in the console log so I know it is actually getting the correct message). It basically just routes the message onto the outbound queue so that client can subscribe to it (if there is a cleaner way to do this please let me know)
<int:channel id="partsChannel" />
<int-jms:message-driven-channel-adapter
id="jmsPartsInbound"
acknowledge="transacted"
destination-name="parts.in"
channel="partsChannel"
connection-factory="jmsConnectionFactory"
/> <!-- error-channel="partsInboundFailedChannel" -->
<int-jms:outbound-channel-adapter
id="jmsPartsOutbound"
destination-name="parts.out"
channel="partsChannel"
connection-factory="jmsConnectionFactory"
pub-sub-domain="true"
>
<int-jms:request-handler-advice-chain>
<int:retry-advice max-attempts="3">
<int:exponential-back-off initial="2000" multiplier="2" />
</int:retry-advice>
</int-jms:request-handler-advice-chain>
</int-jms:outbound-channel-adapter>
Applications subscribe like so:
<int:channel id="partsInboundChannel" />
<int-jms:message-driven-channel-adapter
id="jmsPartsInbound"
acknowledge="transacted"
destination-name="parts.out"
channel="partsInboundChannel"
pub-sub-domain="true"
connection-factory="jmsConnectionFactory"/>
And this is the part that gets the message in the subscriber.
#ServiceActivator(inputChannel = "partsInboundChannel")
public void processPart(final Message message) {
...message.getHeaders does not contain the "Method" header
}
Isn't your issue here in the DefaultJmsHeaderMapper.fromHeaders:
if (value != null && SUPPORTED_PROPERTY_TYPES.contains(value.getClass())) {
try {
String propertyName = this.fromHeaderName(headerName);
jmsMessage.setObjectProperty(propertyName, value);
}
where SUPPORTED_PROPERTY_TYPESare:
private static List<Class<?>> SUPPORTED_PROPERTY_TYPES = Arrays.asList(new Class<?>[] {
Boolean.class, Byte.class, Double.class, Float.class, Integer.class, Long.class, Short.class, String.class });
So, if your method is really of Method type, it will be skipped.
Consider to use its name instead.

Resources