exception handling in int-jpa:updating-outbound-gateway - spring-integration

i have a code
<int-jpa:updating-outbound-gateway
auto-startup="true"
native-query="update Transactions t set t.transaction_Status = :transactionStatus
where t.bank_Reference_Number = :bankReferenceNumber "
entity-manager="entityManager" persist-mode="PERSIST"
use-payload-as-parameter-source="false">
<int-jpa:transactional />
<int-jpa:parameter name="transactionStatus" expression="payload['transactionStatus']" />
<int-jpa:parameter name="bankReferenceNumber" expression="payload['bankReferenceNumber']" />
</int-jpa:updating-outbound-gateway>
i want to add error channel , so that if some exception occurs i can see that.

The error-channel is for flows which works independently of the user: Message Driven Channel Adapter, e.g. <int-jms:message-driven-channel-adapter>, where an Listener Container is started in the infinite loop and does its work actively.
Another sample is Polling Consumer, which runs the polling task periodically, e.g. <int-file:inbound-channel-adapter>, or anyone as a consumer on the QueueChannel. And again: it does that actively, independently of your existence.
The <int-jpa:updating-outbound-gateway> is passive component, therefore it can't do anything until you send a message. Therefore any error caused in this kind of components are thrown to the caller, like it is done in the raw Java method invocation.
Read more about error handling in the Reference Manual.
For your use-case you should consider to add error-channel in the upstream poller if that.
Another case is ExpressionEvaluatingRequestHandlerAdvice, which with its failureChannel may play some kind of error handling for the particular MessageHandler.

The error channel goes on some upstream component (usually whatever starts the flow).
An alternative is to add and ExpressionEvaluatingAdvice to the gateway; see the reference manual.

Related

Spring Integration - how to pass parameters into the method of service activator

I need to pass the parameter in the method called via service-activator. I am able to successfully do this with the help of header-enricher. Below is the working code snippet.
<int:chain input-channel="inChannel">
<int:header-enricher>
<int:header name="routeName" value="TestRoute" />
</int:header-enricher>
<int:service-activator ref="customLoggingRoute"
method="logRoute">
</int:service-activator>
</int:chain>
public Message logRoute(Message m, #Header("routeName") String routeName) {
System.out.println("Inside route: " + routeName);
return m;
}
But I dont want to add anything to header. Is there any alternative by which we can accomplish the same thing without header-enricher.
No, there is no. The Spring Integration contract for invokers is Messaging. So, when we have only a message in between, there is nothing more we can utilize. You can have a complex payload object and transfer data through it, or move your data into headers.
Of course you can consider to use a ThreadLocal since we are in Java and as long as your flow is in the same thread, but this is going to be slightly overhead.
I think you need to start from the theory of the Messaging and come back to us when you understand all those restrictions.
https://www.enterpriseintegrationpatterns.com/

JMS queue not found exception

Oi
I have a bpel process that puts messages in a jms queue and I need to do some specific work if the insertion on the queue fails for some reason.
To test that i disabled the insertion on the EM console but when the bpel tries to insert an exception is raised and i can't catch it.
Any work around for my problem?
This is my response
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header>
<tracking:faultId xmlns:tracking="http://oracle.soa.tracking.core.TrackingProperty">470018</tracking:faultId>
</env:Header>
<env:Body>
<env:Fault>
<faultcode>env:Server</faultcode>
<faultstring>Exception occurred when binding was invoked. Exception occurred during invocation of JCA binding: "JCA Binding execute of Reference operation 'Produce_Message' failed due to: ERRJMS_PROVIDER_ERR. ERRJMS_PROVIDER_ERR. Unable to produce message due to JMS provider internal error. Please examine the log file to determine the problem. ". The invoked JCA adapter raised a resource exception. Please examine the above error message carefully to determine a resolution.</faultstring>
<faultactor/>
<detail>
<exception>Failed to send message to the destination SRVMEModule!SRVS04: Destination is suspended</exception>
</detail>
</env:Fault>
</env:Body>
</env:Envelope>
Your fault looks like a standard binding fault which could be easly caught and handled. Put your jms insert activity inside a scope and you should be able to define error handler(catch all) on the scope level.

How to set payload as constructor-arg value in service-activator

I've started with SI and kind of stuck right now as we want to use SI in one of our existing project avoiding changes where we can.
A bean which we would be using as a service activator accepts an constructor argument of a java object.
that object is in the payload but then I'm unable to set it using inner bean usage of service-activator
<service-activator input-channel="ADMIN_TEST_CONNECTION" method="testConnection">
<beans:bean class="mypackage.request.AdminRequestProcessor">
<beans:constructor-arg value="payload"/>
</beans:bean>
</service-activator>
it's complaining about Could not convert argument value of type [java.lang.String] to required type.
Please help in how to access payload and set it as an constructor argument.
If I go via non- constructor arg route and change existing java object then it works with this call in the service activator
expression="#bean.testConnection(payload)"/>
but I don't wish you to change the existing java code until there is no other way.
I think you don't have choice unless change something or add code around existing.
Service-Activator performs its functionality against each incoming message in the input-channel. And that functionality is exactly method invocation where Message is used as a context for method arguments.
Not sure what you are going to do with that payload, but that doesn't look correct to use statefull object like your AdminRequestProcessor. Just don't forget that you may have many incoming message, but service-activator should be consistent.
Plus don't forget that <beans:bean> is singleton, so your AdminRequestProcessor is instantiated only once.
Looking to your sample I'd suggest something like this:
expression="new AdminRequestProcessor(payload).testConnection()"/>
If you really don't want to change anything in your code.
Everything rest you can find in the Reference Manual.

Message not available at end of Aggregator

I have built a Spring Integration application and transferred some messages around and tried to bring them together with an Aggregator. The application reaches the Aggregator but does not deliver exactly what I want specifically I do not release the group and move onto the next step.
My problem however is my aggregator doesn't have the original message (from before the Splitter). My aggregator is defined as follows
<int:aggregator input-channel="deirBoxProcessorToAggregatorChannel"
ref="loggingAggregator" method="logAggregation"
output-channel="aggregatorToTransformer"
expire-groups-upon-completion="true"/>
And the code inside it is as follows..
public class LoggingAggregator {
private static final Logger LOGGER = Logger.getLogger(LoggingAggregator.class);
public void logAggregation(Message<File> message) {
LOGGER.info("Have aggregated messsages. Will archive");
}
My message in that method, although it enters it, is always null.
Application Context/XML Spring Integration definition
<int:splitter input-channel="transformerToSplitterChannel"
ref="fileToMessageSplitter"
output-channel="shippedSplitterToRouterChannel"
method="split" apply-sequence="true"/>
<!-- Now use a router to determine which Message builder these messages are sent onto -->
<int:router input-channel="shippedSplitterToRouterChannel"
ref="shippedToTypeRouter" />
<int:transformer input-channel="deirShippedBoxToTransformerChannel"
ref="shippedBoxTransformer" method="transform" output-
channel="deirShippedTransformerToProcessorChannel"/>
<int:service-activator id="wellFormedShippedBoxProcess"
input-channel="deirShippedTransformerToProcessorChannel"
output-channel="deirBoxProcessorToAggregatorChannel"
ref="deirShippedFileProcessor" method="processBox" />
<int:service-activator id="malformedShippedBoxProcess"
input-channel="deirMalformedShippedTransformerToProcessorChannel"
output-channel="deirBoxProcessorToAggregatorChannel"
ref="deirShippedFileProcessor"
method="processMalformedBox" />
<int:aggregator input-channel="deirBoxProcessorToAggregatorChannel"
ref="loggingAggregator" method="logAggregation"
output-channel="aggregatorToTransformer"
expire-groups-upon-completion="true"/>
<int:transformer expression="headers.file_originalFile"
input-channel="aggregatorToTransformer"
output-channel="transformerToArchiver" />
<int-file:outbound-channel-adapter id="deirArchiver"
channel="transformerToArchiver"
directory="${dataexhange.springintg.refactor.archive.dir}"
delete-source-files="true"/>
The process gets all the way to the Aggregator but does not seem to make it past to the Transformer or OutboundChannelAdapter archiver.
Thank you in advance.
Your LoggingAggregator isn't correct. I recommend you to read the Reference Manual.
Your logAggregation method should be like this:
public File logAggregation(List<String> lines) {
LOGGER.info("Have aggregated messsages. Will archive");
// Create Files from lines
return file;
}
It is a main method of Aggregator: to get a list of objects and return one object.
Artem's answer is correct. I mistakenly thought that the objects I returned to the aggregator would be of type that were sent off by the splitter. You can follow how through debugging I came to that realisation in the comments to Artem's answer.
I did see somewhere, probably in the manual you can in fact return a type that can be cast from the channel that feeds into the aggregator.
With that understanding I could in fact return Object, and cast back up to the required type for use in the logging object I would use either subsequent to or as part of the aggregator.

How to handle null destination on message driven channel adapter

I need the Spring Integration configuration to handle the case where there will be a variable number of queues which the application will receive messages on.
Have tried the following configuration:
<int-jms:message-driven-channel-adapter id="dsToT2"
destination-name="#{tConfigurer.getDsToTQueues().values().toArray().length>2?
dsConfigurer.getDsToTQueues().values().toArray()[2]:null}"
connection-factory="connectionFactory"
channel="ackToTChannel"/>
but, if the destination-name resolves to null, the following exception is thrown:
java.lang.IllegalArgumentException: 'destinationName' must not be null
What is the best way to handle this scenario?
Thanks
So, the problem is here that you get IllegalArgumentException on application startup.
If really don't know if your detination will be null or not, you shoudl do some Java code:
mark your <int-jms:message-driven-channel-adapter> with auto-startup="false"
Introduce separate bean for DefaultMessageListenerContainer with autoStartup=false too, and inject it to the <int-jms:message-driven-channel-adapter>
As far as destination-name is a property of that DefaultMessageListenerContainer you should right some code to resolve your destination on application startup and inject the value (if any) to the container bean.
And call start() of <int-jms:message-driven-channel-adapter>. It is a AbstractEndpoint bean with id dsToT2
Note, you can't provide null to the destination-name attribute. Your AC will fail on startup when it tries to populate bean properties. In this case will be called AbstractMessageListenerContainer#setDestinationName, which, in turn, does the check
Assert.notNull(destinationName, "'destinationName' must not be null");.
However, you can try to provide empty string '' instead of null and add similar SpEL condition for auto-startup attribute.
HTH

Resources