Camel setBody using Spring configuration - spring

I have built a Java Camel Timer-JMS route using:
context.addRoutes(new RouteBuilder() {
public void configure() {
from("timer:foo?period=1s").setBody(body().
append("Message at ${date:now:yyyy-MM-dd HH:mm:ss}")).to("jms:queue:activemq/queue/TestQueue");
}
});
Now I need to turn it to Spring.
<camel:route>
<camel:from uri="timer:foo?period=1s" />
<camel:to uri="jms:queue:activemq/queue/TestQueue" />
</camel:route>
I'm missing the equivalent in Spring XML configuration of the expression:
setBody(body().append("Message at ${date:now:yyyy-MM-dd HH:mm:ss}")

In Spring you can use the simple language, to build such messages. In fact you can also do this in Java, it the same.
<setBody>
<simple>${body}Message at ${date:now:yyyy-MM-dd HH:mm:ss}</simple>
</setBody>
Mind that the timer sends an empty/null body. So you may see "null" in the message.
About simple see: http://camel.apache.org/simple

Related

Tracing input & output of message channels of Spring Integration

I have build a small application using Spring Integration. I have also written some jUnit for more service and other classes.
I have used XML configuration for the channel and en-point configuration, I am wondering if I can test the input and output of a perticular channel.
Is there a way we can test the input and output of the channels?..
Update
I am trying to the below flow. How shall i proceed?
<int:channel id="getPresciption" />
<int:channel id="respPrescription" />
<int:channel id="storePrcedureChannell" />
<int-http:inbound-gateway
request-channel="getPresciption" reply-channel="respPrescription"
supported-methods="GET" path="/getAllPresciption">
<int-http:request-mapping
consumes="application/json" produces="application/json" />
</int-http:inbound-gateway>
<int:service-activator
ref="prescriptionServiceActivator" method="buildPrescription"
input-channel="getPresciption" output-channel="storePrcedureChannell" />
<int:service-activator
ref="prescriptionServiceActivator" method="storePrescription"
input-channel="storePrcedureChannell"></int:service-activator>
SO how can i write the Test contex?
Let below are the methods which are called by channel flow.
public Message<List<Prescription>> buildPrescription() {
//Do some processing
}
public Message<List<Prescription>> storePrescription(Message<List<Prescription>> msg) {
//Do something and return the List.
}
First of all you can take a look into the Spring Integration Testing Framework: https://docs.spring.io/spring-integration/reference/html/testing.html#test-context
And then use a MockIntegration.mockMessageHandler() together with the MockIntegrationContext.substituteMessageHandlerFor() to replace a real handler in the endpoint and verify incoming data from the channel.
If that is still hard for you, you always can inject those channels into your test class and add to them a ChannelInterceptor and verify messages in its preSend() implementation.

Apache Camel -- Reference type constants in Spring DSL

I'm trying to define a publish operation to Hazelcast topic using Spring DSL
<from uri="direct:inbound" />
<onCompletion>
<log message="onCompletion:- ${body}" />
<setHeader headerName="${type:org.apache.camel.component.hazelcast.HazelcastConstants.OPERATION}">
<simple>${type:org.apache.camel.component.hazelcast.HazelcastConstants.PUBLISH_OPERATION}</simple>
</setHeader>
<to uri="hazelcast:topic:foo" />
</onCompletion>
<log message="${body}" />
The above route works, but I have to use long SIMPLE scripts like "${type:org.apache.camel.component.hazelcast.HazelcastConstants.OPERATION}" to ref a constant value. Is there any simpler or short form for this?
I tried to define a spring bean for HazelcastConstants class and ref it through SIMPLE scripts as below but it's not working with MethodNotFoundException "Method with name: OPERATION not found on bean"
<bean id="hazelcastConstants" class="org.apache.camel.component.hazelcast.HazelcastConstants" />
... ...
<simple>${bean:hazelcastConstants.OPERATION}</simple>
Your bean workaround would work, if you defined a bean contained a method returning the constant in question, e.g.:
public class ContantRetriever() {
public String getHazelCastOperation() {
return org.apache.camel.component.hazelcast.HazelcastConstants.PUBLISH_OPERATION;
}
}
Your Spring context:
<bean id="hazelcastConstants" class="yourpackage.ContantRetriever"/>
<simple>${bean:hazelcastConstants.getHazelCastOperation}</simple>
If that is no good for you, I am afraid you are stuck with the long form of accessing constants.

DeferredResult from JMS Request/Reply with ListenableFuture

I was looking for opportunities to improve the Rest Api we have exposed to external clients. During that exercise, I found I am barely taking advantage of the decision our integration team took to have backend integrations with JMS request/reply instead of traditional blocking SOAP request/reply.
Currently all the interactions to message broker are done using jmsOutboundGateway, because of which requesting thread has to wait for completion. In order to scale RestAPI, I want to send JMS reply using DeferredResult from Spring MVC controller. The controller interaction with message broker is depicted below:
Controller --> GatewayProxy --> JMSOutboundGateway
I am looking for opportunities to use ListenableFuture as return type of GatewayProxy, but I am unable to find a proper mean of achieving it using spring integration.
Below is the integration flow I am calling from controller:
<int:gateway
service-interface="ae.emaratech.ngx.service.PermitSearchService"
default-request-channel="permit_search_input_channel"
default-reply-timeout="${broker.jms.gateway.min.consumers}"/>
<int:channel id="permit_search_input_channel" />
<int:chain input-channel="permit_search_input_channel">
<int:header-enricher>
<int:header name="person_number" expression="payload"/>
</int:header-enricher>
<int:transformer expression="#formatString(#api_messages['FIND_PERM_BY_PERSNO_MSG'],headers)"/>
<int:header-filter header-names="JMS_*,jms_*,priority" pattern-match="true" />
<int:header-enricher>
<int:header name="jms_type" type="java.lang.String" value="1" overwrite="true"/>
</int:header-enricher>
<jms:outbound-gateway
request-destination="permitsInboundQueue"
reply-destination="permitsOutboundQueue"
receive-timeout="${broker.jms.gateway.timeout}"
correlation-key="Correlation_ID"
connection-factory="brokerConnectionFactory">
<jms:reply-listener concurrent-consumers="${broker.jms.gateway.min.consumers}" max-concurrent-consumers="${broker.jms.gateway.max.consumers}"/>
</jms:outbound-gateway>
<int-xml:xpath-filter throw-exception-on-rejection="true">
<int-xml:xpath-expression expression="not(boolean(/*/ErrorDetails))"/>
</int-xml:xpath-filter>
<int-xml:xslt-transformer
xsl-resource="classpath:/META-INF/spring/integration/permit-to-json.xsl"
result-type="StringResult" >
</int-xml:xslt-transformer>
<int:transformer expression="payload.toString()"/>
</int:chain>
Not sure what problem you have, but the feature looks like:
ListenableFuture<String> result = this.asyncGateway.async("foo");
result.addCallback(new ListenableFutureCallback<String>() {
#Override
public void onSuccess(String result) {
...
}
#Override
public void onFailure(Throwable t) {
...
}
});
It is available since version 4.1.

CamelJdbcColumnNames header missed

I try to use the jdbc component from camel. I found the documentation here: http://camel.apache.org/jdbc.html.
It works well as the result is available from the database but there is no header in the queued answer called CamelJdbcColumnNames as mentioned in the documentation.All i can see is CamelJdbcRowCount. My camel version is 2.15.1.Do i have to turn a switch to enable this?
Here is an extract of my spring-config.xml:
<bean id="ds" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
<property name="driverClass" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:#myhost:1521:mydbsid"/>
<property name="username" value="myuser"/>
<property name="password" value="mypass"/>
</bean>
<route id="route db">
<from uri="file://data/inbox" />
<to uri="jdbc:ds" />
<to uri="jms:sqlret" />
</route>
EDIT:
To exclude the jms i added a Processor. With this i want to debug the message header. This is a new extract of my spring-config.xml:
<bean id="jdbccheck" class="mypackage.JdbcCheck"></bean>
<route id="route db">
<from uri="file://data/inbox" />
<to uri="jdbc:ds" />
<process ref="jdbccheck"/>
<to uri="jms:sqlret" />
</route>
The Processor code:
public class JdbcCheck implements Processor {
private static final Logger LOG = Logger.getLogger(JdbcCheck.class.getName());
#Override
public void process(Exchange exchange) throws Exception {
LOG.info(exchange.getIn().getHeaders().toString());
}
}
The log message:
{breadcrumbId=ID-chris-HP-50597-1429955241877-0-1, CamelFileAbsolute=false, CamelFileAbsolutePath=C:\daten\chris\source\netbeans\GbLuna\data\inbox2\in.sql, CamelFileContentType=null, CamelFileLastModified=1429953640254, CamelFileLength=36, CamelFileName=in.sql, CamelFileNameConsumed=in.sql, CamelFileNameOnly=in.sql, CamelFileParent=data\inbox2, CamelFilePath=data\inbox2\in.sql, CamelFileRelativePath=in.sql, CamelJdbcRowCount=837}
The last var-/value pair is CamelJdbcRowCount=837 which seems to me that it works somehow. But for further processing i want to deal with the column names. So: how to get CamelJdbcColumnNames?
Ah okay got it now, its because you send the data to a JMS endpoint. And JMS specification only support a number of data types for JMS headers/properties. And that is usually String, numbers and primitive types.
You can read more about this on the Camel JMS documentation page, and from the JMS spec/javadoc.
The column names header is stores as a header of Java collection type and that is not supported by JMS.
http://camel.apache.org/jms
If you enable the Camel tracer you should be able to see the header before the message is routed to the JMS endpoint: http://camel.apache.org/tracer

Spring splitter/aggregator handling exceptions

Version : spring-integration-core - 2.2.3
Here is the simplified version of my splitter/aggregator setup.
<task:executor id="taskExecutor" pool-size="${pool.size}"
queue-capacity="${queue.capacity}"
rejection-policy="CALLER_RUNS" keep-alive="120"/>
<int:channel id="service-requests"/>
<int:channel id="service-request"/>
<int:channel id="channel-1">
<int:dispatcher task-executor="taskExecutor" failover="false"/>
</int:channel>
<int:channel id="channel-2">
<int:dispatcher task-executor="taskExecutor" failover="false"/>
</int:channel>
<int:gateway id="myServiceRequestor" default-reply-timeout="${reply.timeout}"
default-reply-channel="service-aggregated-reply"
default-request-channel="service-request"
service-interface="com.blah.blah.MyServiceRequestor"/>
<int:splitter input-channel="service-request"
ref="serviceSplitter" output-channel="service-requests"/>
<!-- To split the request and return a java.util.Collection of Type1 and Type2 -->
<bean id="serviceSplitter" class="com.blah.blah.ServiceSplitter"/>
<int:payload-type-router input-channel="service-requests" resolution-required="true">
<int:mapping
type="com.blah.blah.Type1"
channel="channel-1"/>
<int:mapping
type="com.blah.blah.Type2"
channel="channel-2"/>
</int:payload-type-router>
<!-- myService is a bean where processType1 & processType2 method is there to process the payload -->
<int:service-activator input-channel="channel-1"
method="processType1" output-channel="service-reply" requires-reply="true"
ref="myService"/>
<int:service-activator input-channel="channel-2"
method="processType2" output-channel="service-reply" requires-reply="true"
ref="myService"/>
<int:publish-subscribe-channel id="service-reply" task-executor="taskExecutor"/>
<!-- myServiceAggregator has a aggregate method which takes a Collection as argument(aggregated response from myService) -->
<int:aggregator input-channel="service-reply"
method="aggregate" ref="myServiceAggregator"
output-channel="service-aggregated-reply"
send-partial-result-on-expiry="false"
message-store="myResultMessageStore"
expire-groups-upon-completion="true"/>
<bean id="myResultMessageStore" class="org.springframework.integration.store.SimpleMessageStore" />
<bean id="myResultMessageStoreReaper" class="org.springframework.integration.store.MessageGroupStoreReaper">
<property name="messageGroupStore" ref="myResultMessageStore" />
<property name="timeout" value="2000" />
</bean>
<task:scheduled-tasks>
<task:scheduled ref="myResultMessageStoreReaper" method="run" fixed-rate="10000" />
</task:scheduled-tasks>
If the processType1/processType2 method in mySevice throws a RuntimeException, then it tries to send the message to an error channel(i believe spring does it by default) and the message payload in error channel stays on in heap and not getting garbage collected.
Updated More Info:
For my comment on error channel. I debugged the code and found that ErrorHandlingTaskExecutor is trying to use a MessagePublishingErrorHandler which inturn sending the message to the channel returned by MessagePublishingErrorHandler.resolveErrorChannel method.
Code snippet from ErrorHandlingTaskExecutor.java
public void execute(final Runnable task) {
this.executor.execute(new Runnable() {
public void run() {
try {
task.run();
}
catch (Throwable t) {
errorHandler.handleError(t); /// This is the part which sends the message in to error channel.
}
}
});
}
Code snipper from MessagePublishingErrorHandler.java
public final void handleError(Throwable t) {
MessageChannel errorChannel = this.resolveErrorChannel(t);
boolean sent = false;
if (errorChannel != null) {
try {
if (this.sendTimeout >= 0) {
sent = errorChannel.send(new ErrorMessage(t), this.sendTimeout);
.....
When i take a heap dump, I always see the reference to the payload message(which i believe is maintained in the above channel) and not getting GC'ed.
Would like to know what is the correct way to handle this case or if i'm missing any in my config?
Also is it possible to tell spring to discard the payload(instead of sending it to error channel) in case of any exception thrown by the service activator method?
Looking forward for your inputs.
Thanks.
You don't have an error-channel defined on your gateway so we won't send it there, we'll just throw an exception to the caller.
However, the partial group is sitting in the aggregator and will never complete. You need to configure a MessageGroupStoreReaper as shown in the reference manual (or set a group-timeout in Spring Integration 4.0.x) to discard the partial group.

Resources