I am using Spring Integration 4.1.2.
In the program flow, at the end of a process I need to send an email. I am using the following:
<int:payload-type-router input-channel="response.in">
<int:mapping type="java.lang.String" channel="response.out"/>
<int:mapping type="org.springframework.mail.SimpleMailMessage" channel="mail.out"/>
</int:payload-type-router>
<mail:outbound-channel-adapter channel="mail.out" mail-sender="mailSender"/>
This is working fine.
I want to also handle the situation when the mail server is down.
I've tried the following solution but it does not work.
<int:chain input-channel="errorChannel" output-channel="emailErrorChannel">
<int:transformer ref="errorUnwrapper" />
</int:chain>
And the unwrapper:
#MessageEndpoint
public class ErrorUnwrapper {
#Transformer
public Message<?> transform(final ErrorMessage errorMessage) {
Message<?> failedMessage = ((MessagingException) errorMessage.getPayload()).getFailedMessage();
return failedMessage;
}
}
But this is not working. I want to catch the exception and send a meaningful response back to the user instead of a stacktrace. And I want to do this within Spring Integration. Otherwise I'll have to write a Java mail service call with a service-activator instead of the SI mail extension.
Or is it a one-way component? I've just found this blog post doing the same. I've also read this but got nowhere.
You likely need to add the errorChannel to whatever starts you flow; you need to show us the rest of your configuration.
Alternatively, you can add an ExpressionEvaluatingAdvice to the mail adapter; see this sample for an example of using the advice.
Related
I am new in Spring Integration and maybe this question is trivial. I am looking at the example (https://github.com/spring-projects/spring-integration-samples/tree/master/basic/amqp) creating RabbitMq message from stdin:
<int-stream:stdin-channel-adapter id="consoleIn"
channel="toRabbit">
<int:poller fixed-delay="1000" max-messages-per-poll="1" />
</int-stream:stdin-channel-adapter>
<int:channel id="toRabbit" />
<int-amqp:outbound-channel-adapter
channel="toRabbit" amqp-template="amqpTemplate"
exchange-name-expression="payload.toLowerCase() == 'nack' ? 'badExchange' : 'si.test.exchange'"
routing-key-expression="payload.toLowerCase() == 'fail' ? 'badKey' : 'si.test.binding'"
confirm-correlation-expression="payload"
confirm-ack-channel="good"
confirm-nack-channel="errors"
return-channel="returns" />
What if we need to have a custom message, produced in Java code. What will be the proper elegant code? The bean to populate the message is simplified:
package com.mycompany.domain.price;
public class UpdateMessage implements Serializable {
Date effStartDate;
Date effEndDate;
Long orderId = -1;
String customerFullName;
...
}
See this answer; although it's talking about Axis instead of RabbitMQ; the same techniques apply.
Since your UpdateMessage implements Serializable, the standard message converter will take care of the conversion to a byte[] for you.
Sending a message with no reply, your gateway interface method might be
public void send(UpdateMessage msg);
In which case you'd you use an outbound channel adapter. If you want to get a reply, use an amqp outbound gateway and the service interface might look like
public UpdateResult send(UpdateMessage msg);
If you're not using Serializable objects, use of a json converter might be appropriate instead.
I wanted to know is there any way to add properties to JMS text Message in Spring Integration.
For example if we use normal JMS code we can always set properties to it using the below code.
message.setStringProperty( "AuctionType", "Reverse" );
Edit
I tried to add the header as given in Spring document but now i am getting same message Id for each message, but as i am intended to use it as ID, so i need it to be different for each message.
Below is my snapshot of Spring configuration.
<bean class="com.learn.util.RandomMsgId" id="randomMsgId" factory-method="getRndMsgId" scope="prototype"/>
<int:header-enricher input-channel="xmlToJMS"
output-channel="xmltoJMSwithId">
<int:header name="MsgId" ref="randomMsgId"/>
</int:header-enricher>
Use a <header-enricher/> to add custom headers to the spring integration message and they will be mapped to JMS headers.
See the documentation and here.
I have already created a simple tcp server with spring integration which keeps a connection alive and responses to each request during the connection.
In that requestMethod, I'm also able to read the MessageHeder to get the connectionId.
Now I want to send messages from the server to the client.
As far as i understood the documentation i need to put the connectionid in the MessageHeader and then send the message. But I can't figure out how to do the latter one. I have the Message ready but how do i send/push it out?
Here is my xml-configuration:
<bean id="lfSerializer" class="org.springframework.integration.ip.tcp.serializer.ByteArrayLfSerializer"/>
<int-ip:tcp-connection-factory
id="socketserver"
type="server"
port="30124"
using-nio="true"
deserializer="lfSerializer"
serializer="lfSerializer"
single-use="false"/>
<int-ip:tcp-inbound-channel-adapter id="inboundServer"
channel="inputChannel"
connection-factory="socketserver"/>
<int-ip:tcp-outbound-channel-adapter id="outboundServer"
channel="outputChannel"
connection-factory="socketserver"
/>
<int:channel id="inputChannel">
<int:interceptors>
<int:wire-tap channel="logger"/>
</int:interceptors>
</int:channel>
<int:channel id="outputChannel">
<int:interceptors>
<int:wire-tap channel="logger"/>
</int:interceptors>
</int:channel>
<int:logging-channel-adapter id="logger" level="DEBUG" log-full-message="true"/>
<int:service-activator input-channel="inputChannel"
output-channel="outputChannel"
ref="echoService"
method="test"/>
<bean id="echoService"
class="com.examples.EchoService" />
I also tried to create a bean and another serviceactivator for output, then autowired that bean and called it's "send" method, but I don't know what to implement in that send-method to send out a message.
If it's a simple request/response scenario use an inbound gateway instead of channel adapters and the framework will take care of the correlation for you. This is used in the sample app. Simply have your POJO method return the reply payload.
If you want to send arbitrary messages to the client (i.e. NOT request/reply but, say, in, out, out, in, out, out, out etc) then, yes, you need to build the messages yourself, inserting the ip_connectionId header.
To send them, there are several options:
Inject outputChannel into your code
#Autowired
private MessageChannel outputChannel;
Use a MessagingTemplate to send to the channel (or simply call its send(Message<?> message) method directly).
Or
Use a MessagingGateway with a void return method and inject the gateway into your code.
EDIT:
Note, that if you want to start sending messages before receiving anything, you can obtain the connection id via the connection opened event.
I'm trying to send a simple POST request to a REST endpoint. I have a simple pojo that I would like to send in the payload as JSON. Here is the pojo (note I'm using Spring Integration with grails so the pojo/service are in Groovy):
class Person implements Serializable {
String name
}
Here is my gateway:
public interface PersonGateway {
Person savePerson(Person person)
}
Here are the important parts of the wiring:
<int:channel id="requestChannel" />
<int:channel id="responseChannel" />
<int:header-enricher input-channel="requestChannel">
<int:header name="Content-Type" value="application/json" />
</int:header-enricher>
<int:gateway id="PersonGateway"
service-interface="com.example.PersonGateway"
default-request-channel="requestChannel"
default-reply-channel="responseChannel">
<int:method name="savePerson" />
</int:gateway>
<int-http:outbound-gateway url="http://127.0.0.1:8000/person"
http-method="POST"
message-converters="jsonConverter"
expected-response-type="com.example.Person"
request-channel="requestChannel"
extract-request-payload="false"/>
This POST request never reaches that service but doesn't throw any exceptions. When I log all levels the only thing I get that looks like a clue is:
2014-03-30 16:35:07,313 [main] DEBUG
outbound.HttpRequestExecutingMessageHandler - Unable to attempt
conversion of Message payload types. Component
'org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler#0'
has no explicit ConversionService reference, and there is no
'integrationConversionService' bean within the context.
Other than that it doesn't give me much. I've been trying little things all day and can't seem to get anywhere. Anyone see what I am missing? Thanks!
Unable to attempt conversion of Message payload types.
Don't worry: it doesn't have value for you. In most cases we can live in our application without that integrationConversionService. Here it is needed for expressions. Since you don't use any expression, that WARN doesn't hurt you.
You say that you are going to send POJO as JSON, then you shouldn't use extract-request-payload="false". With false the <int-http:outbound-gateway> places to the request body entire Message<?> and changes content-type to application/x-java-serialized-object
Hope your jsonConverterdoes the correct stuff around POJO <-> JSON
I have a simple request reply test implemented using the following configuration:
<int:gateway id="myGateway"
service-interface="TestGateway"
default-request-channel="sendingChannel"
default-reply-channel="replyChannel"
default-reply-timeout="2000"
/>
<int:channel id="sendingChannel" />
<int:channel id="replyChannel" />
<int-jms:outbound-gateway id="myJmsGateway"
connection-factory="jmsConnectionFactory"
request-channel="sendingChannel"
request-destination-name="outQueue"
reply-channel="replyChannel"
reply-destination-name="outQueueReply"
receive-timeout="60000"
/>
and the Interface:
public interface TestGateway {
#Gateway
public String requestReply(#Header("myHeaderKey") String headerValue, String data);
}
While the above configuration does "work" I have the following reservations.
The configuration feels redundant. Extra gateway and two extra channels required. Both gateways implement a reply timeout (although the int:gateway timeout doesn't fire when connected to a int-jms:outbound-gateway).
The semantics of the gateway method change depending on what is implementing the request/reply. On Timeout the int-jms:outbound-gateway will throw an exception, which will propagate to the user of TestGateway. If the config is changed to replace int-jms:outbound-gateway the int:gateway will return null.
Given this the client code has to both handle null and the exception in the same way.
Are there any better ways to wire up the gateways? One option would be to change the int:channel's to PollableChannel's which solves problem 2 at the expense of an extra thread pool.
You don't need to configure reply channels; by default the jms gateway (with no reply channel) will return the message to the inbound gateway automatically.
When using direct channels, the messaging gateway's timeout only starts when the thread tries to receive any reply that was returned from the flow.
You can avoid the different semantics (null Vs exception) by adding an error-channel to the inbound gateway.
It's important to understand that myGateway isolates your client from the messaging system, you code to the interface only. Of course you could inject the JMS gateway directly but then you've added dependencies to your code. With a messaging gateway, you can change technologies without making any changes to your client code. You can also unit test your code by providing a test implementation of TestGateway. This is a powerful feature of Spring Integration.