I have the following setup connected as follows:
1) response & request channel
2) transformer for ws response/request to a system value object
3) WS request/response channel
4) Outbound gateway
<!-- ~~~~~~~~~~~~~~~~~ -->
<!-- integration layer -->
<!-- ~~~~~~~~~~~~~~~~~ -->
<int:channel id="getStatusRequestChannel"/>
<int:channel id="getStatusResponseChannel"/>
<int:channel id="getStatusWSRequestChannel"/>
<int:channel id="getStatusWSResponseChannel"/>
<!-- ~~~~~~~~~~~~~~~~~~ -->
<!-- gateway definition -->
<!-- ~~~~~~~~~~~~~~~~~~ -->
<int:gateway id="mnpGateway" service-interface="com.iquest.play.integration.mnp.MNPGateway">
<int:method name="getMNPStatus" request-channel="getStatusRequestChannel" reply-channel="getStatusResponseChannel"/>
</int:gateway>
<!-- ~~~~~~~~~~~~~~ -->
<!-- channel chains -->
<!-- ~~~~~~~~~~~~~~ -->
<int:chain input-channel="getStatusRequestChannel" output-channel="getStatusWSRequestChannel">
<int:transformer ref="getStatusTransformer" method="transformMNPStatusRequest"/>
</int:chain>
<int:chain input-channel="getStatusWSResponseChannel" output-channel="getStatusResponseChannel">
<int:transformer ref="getStatusTransformer" method="transformMNPStatusResponse"/>
</int:chain>
<!-- ~~~~~~~~~~~~~~~~ -->
<!-- outbound gateway -->
<!-- ~~~~~~~~~~~~~~~~ -->
<int-ws:outbound-gateway id="getStatusOutboundGW"
request-channel="getStatusWSRequestChannel"
reply-channel="getStatusWSResponseChannel"
marshaller="marshaller"
unmarshaller="marshaller"
destination-provider="mnpUriProvider"/>
This is the WSDL :
<wsdl:operation name="getCaseInfo">
<wsdl:documentation>Message</wsdl:documentation>
<wsdl:input message="tns:GetCaseInfoRequest">
</wsdl:input>
<wsdl:output message="tns:GetCaseInfoResponse">
</wsdl:output>
<wsdl:fault message="tns:GetCaseInfoError" name="getCaseInfoError">
</wsdl:fault>
</wsdl:operation>
How can I catch the Soap Fault?
AFTER EDIT :
I've tried extending a SoapFaultMessageResolver and to overide the method public void resolveFault(WebServiceMessage message) throws IOException
from there I'm trying to throw a custom IntegrationException (that extends IOException) that I will catch in the method that calls the gateway interface. This is the calling method:
try {
gateway.MethodA();
} catch (Exception e) {
/// I was trying to catch IntegrationException
}
The problem is that the caught exception is of type WebServiceIOException that has the root cause IntegrationException, and it triggers a huge error log. So I think this approach isn't right.
The <int-ws:outbound-gateway> is fully based on WebServiceTemplate from Spring WS, so there is no any stops to do the same with Soap Fault in Spring Integration.
By default it will be a WebServiceException, which is thrown from WebServiceTemplate.sendAndReceive and propagated to the MessageHandler, which, in turn, throws it to the caller or send to the error-channel as a Message payload.
As soon as you use <int:gateway> that Exception might be catched on the method invocation.
If you want to do some logic with that Fault before rethrow you can inject fault-message-resolver as an implementation of FaultMessageResolver to the <int-ws:outbound-gateway>.
We can handle soap fault message during unmarshel and throw the appropriate message to error handler.
This article explains clearly how to handle soap fault message. Hope this might be helpful
http://blog.hostmasterzone.com/how-to-unmarshal-soap-fault-in-spring-integration-web-service/
<bean id="hmzJaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="contextPaths">
<list>
<value>org.hmz.request.types</value>
</list>
</property>
</bean>
<bean id="custJaxbUnMarshaller" class="com.jay.hmz.util.CustJaxbUnMarshaller" >
<property name="contextPaths">
<list>
<value>org.hmz.request.types</value>
</list>
</property>
</bean>
<int:chain input-channel="channel.in" output-channel="channel.out">
<int:transformer method="transformParentRequestById"><bean class="org.jay.hmz.api.transformers.OrderTransformer"/></int:transformer>
<int-ws:header-enricher><int-ws:soap-action value="${order.request.uri}"/></int-ws:header-enricher> <int-ws:outbound-gateway interceptors="hmzSecurityInterceptor" mapped-request-headers="GUID, USER_REF" request-callback="hmzWebServiceMessageCallback" unmarshaller="custJaxbUnMarshaller" marshaller="hmzJaxbMarshaller" uri="${order.request.uri}"/>
<int:transformer method="transformRetrieveParentOrderResponse"><bean class="org.jay.hmz.api.transformers.OrderTransformer"/></int:transformer>
</int:chain>
<int:service-activator input-channel="requestErrorChannel" output-channel="response.out" ref="requestErrorHandler" method="handleFailedOrderRequest"/>
<bean id="requestErrorHandler" class="org.jay.hmz.api.errorhandler.RequestErrorHandler"/>
public class CustJaxbUnMarshaller extends Jaxb2Marshaller {
#Override
public Object unmarshal(Source source, MimeContainer mimeContainer)
throws XmlMappingException {
LOGGER.debug("Inside Custom JaxbWrapper unmarshal");
Object mimeMessage = new DirectFieldAccessor(mimeContainer)
.getPropertyValue("mimeMessage");
Object unmarshalObject = null;
if (mimeMessage instanceof SaajSoapMessage) {
SaajSoapMessage soapMessage = (SaajSoapMessage) mimeMessage;
String faultReason = soapMessage.getFaultReason();
if (faultReason != null) {
throw convertJaxbException(new JAXBException(faultReason));
} else {
unmarshalObject = super.unmarshal(source, mimeContainer);
}
}
return unmarshalObject;
}
}
Related
I have an activemq queue where messages are being sent to and the application uses Spring JmsTemplate receiveSelected(selector) to receive the messages synchronously. Message is processed before it is acknowledged. If the broker or the application shuts down before acknowledging the message while it is being processed the message needs to be resent or redelivered without getting lost. My understanding is with client_acknowledgement messages gets resent if not acknowledged also.
Configuration
<bean id="jmsConnection" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="broker" value="tcp://localhost:61616" />
<property name="redeliveryPolicy" ref="redeliveryPolicy" />
</bean>
Redelivery Policy:
<bean id="redeliveryPolicy" class="org.apache.activemq.RedeliveryPolicy">
<property name="initialRedeliveryDelay" value="1000" />
<property name="redeliveryDelay" value="10000" />
<property name="maximumRedeliveries" value="2" />
<property name="useExponentialBackOff" value="true" />
<property name="backOffMultiplier" value="5" />
</bean>
JmsTemplate:
<bean id="jmstemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory">
<bean class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="jmsConnection" />
</bean>
</property>
<property name="receiveTimeout" value="5000"/>
<property name="defaultDestinationName" value="messageQueue"/>
<property name="explicitQosEnabled" value="true"/>
<property name="sessionAcknowledgeMode" value="2"/>
<property name="sessionTransacted" value="false"/>
</bean>
Session call back to receive message:
Object getProcessedMessageObject {
return jmsTemplate.execute(session -> {
#Override
public Object doInJms (Session session) throws JMSException {
Object tmp = null;
MapMessage msg = (MapMessage) jmsTemplate.receiveSelected(selector);
try {
if (msg != null) {
MapMessage receivedMsg = msg;
tmp = processMsg(receivedMsg) if (tmp != null) {
msg.acknowledge();
}
}
} catch (JMSException) {
throw new RuntimeException();
} return tmp;
}
});
}
I am getting "Consumer is closed" when msg.acknowledge() is called. When I stop and restart my application the messages are not redelivered as they are not acknowledged. I'm trying to understand what I am missing and how to make it work.
I am developing a REST API which is a #POST and #Consumes both MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON . I need to implement validations for the incoming request. I don't want to have Bean Level validation JSR-303. I need to have a Validation class which handles all the validations and i need to configure interceptor for the incoming XML request before Unmarshalling. I looked into Apache cxf interceptors and it is mainly if you are enabling the Bean Validations. How shall i do it?
I have found the way how to do this without the usage of BeanValidationFeature.
public class YourFilter implements ContainerRequestFilter{
#Override
public void filter(ContainerRequestContext request){
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
final InputStream inputStream = request.getEntityStream();
final StringBuilder builder = new StringBuilder();
try
{
IOUtils.copy(inputStream, outStream);
byte[] requestEntity = outStream.toByteArray();
if (requestEntity.length == 0) {
builder.append("");
} else {
builder.append(new String(requestEntity,"UTF-8"));
request.setEntityStream(new ByteArrayInputStream(requestEntity));
setRequest(builder.toString());
validateYourRequest(builder.toString());
}
}catch (Exception ex) {
logger.log(Level.TRACE,"Error occured while converting the request into Stream",ex.getCause());
}
}
}
And in application.context.xml
<bean id="YourFilter" class="com.test.YourFilter"/>
<jaxrs:server id="restContainer" address="/">
<jaxrs:serviceBeans>
<bean class="com.test.YourController" />
</jaxrs:serviceBeans>
<jaxrs:extensionMappings>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
</jaxrs:extensionMappings>
<jaxrs:providers>
<ref bean="jsonProvider" />
<ref bean="jaxbXmlProvider" />
<ref bean="YourFilter"/>
</jaxrs:providers>
</jaxrs:server>
I am using Spring SimpleMessageListenerConatiner where acknowledgement mode is 2 (client acknowledge) and Queue is Solace.
When I am throwing runtime exception from my unit test, means standalone spring config, messages are redelivering without any issue, but same code is not working when I am deploying my application in JBOSS.
public class MyListener implements MessageListener {
public void onMessage(Message message) {
try {
throw new ConnectionException("Error in Connection");
} catch (ConnectionException e) {
LOGGER.error("Throwing exception...");
throw new MyRuntimeException("Throwing exception");
} finally {
LOGGER.info("Done...");
}
}
Spring config is:
<bean id="solaceMessageListener" class="org.springframework.jms.listener.SimpleMessageListenerContainer">
<property name="connectionFactory" ref="solaceConnectionFactory"/>
<property name="destinationName" value="QueueName"/>
<property name="messageListener" ref="myListener"/>
<property name="concurrency" value="1"/>
<property name="destinationResolver" ref="destinationResolver" />
<property name="sessionAcknowledgeMode" value="2"/>
</bean>
Constaint:
1. I cannot use DefaultMessageListenerContainer
2. Session Transacted true is working but we have not to use it.
I want to list all of files on an FTP server using spring-integration and, for example, print them on screen. I've done something like this:
context:
<int:channel id="toSplitter">
<int:interceptors>
<int:wire-tap channel="logger"/>
</int:interceptors>
</int:channel>
<int:logging-channel-adapter id="logger" log-full-message="true"/>
<int:splitter id="splitter" input-channel="toSplitter" output-channel="getFtpChannel"/>
<int-ftp:outbound-gateway id="gatewayLS"
session-factory="ftpClientFactory"
request-channel="inbound"
command="ls"
expression="payload"
reply-channel="toSplitter"/>
<int:channel id="getFtpChannel">
<int:queue/>
</int:channel>
<bean id="ftpClientFactory"
class="org.springframework.integration.ftp.session.DefaultFtpSessionFactory">
<property name="host" value="${host}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
<property name="clientMode" value="0"/>
<property name="fileType" value="2"/>
<property name="bufferSize" value="10000000"/>
</bean>
Java code:
ConfigurableApplicationContext context =
new FileSystemXmlApplicationContext("/src/citrus/resources/citrus-context.xml");
final FtpFlowGateway ftpFlow = context.getBean(FtpFlowGateway.class);
ftpFlow.lsFiles("/");
PollableChannel channel = context.getBean("getFtpChannel", PollableChannel.class);
variable("tt", channel.receive().toString());
echo("${tt}");
output:
11:09:17,169 INFO port.LoggingReporter| Test action <echo>
11:09:17,169 INFO actions.EchoAction| [Payload=FileInfo [isDirectory=false, isLink=false, Size=3607, ModifiedTime=Tue Jul 15 14:18:00 CEST 2014, Filename=Smoke03_angart30_st40.exi, RemoteDirectory=/, Permiss
ions=-rw-r--r--]][Headers= {replyChannel=org.springframework.integration.core.MessagingTemplate$TemporaryReplyChannel#7829b776, sequenceNumber=1, errorChannel=org.springframework.integration.core.MessagingTempla
te$TemporaryReplyChannel#7829b776, file_remoteDirectory=/, sequenceSize=1, correlationId=49b57f2d-4dbf-4a89-b5b8-0dfb15bca2be, id=0a58ad65-74b4-4aae-87be- aa6034a41776, timestamp=1405501757060}]
11:09:17,169 INFO port.LoggingReporter| Test action <echo> done
11:09:17,169 INFO port.LoggingReporter| TEST STEP 1/1 done
The output is fine, but what should I do to print this information when I don't know how many files are stored on the FTP? (this code prints only one file). I've tried checking if channel.receive() is null but the test just freezes.
Since you send the result of LS to the <splitter>, your getFtpChannel will receive FileInfo<?> objects one by one.
To print them all you really should have an infinite loop:
while (true) {
variable("tt", channel.receive().toString());
echo("${tt}");
}
To stop the app you should provide some shoutDownHook or listen something from console input.
Another point, that it is bad to block your app with infinite receive().
There is na overloaded method, which applies a timeout param. The last one might be useful to determine the end of your loop:
while (true) {
Message<?> receive = channel.receive(10000);
if (receive == null) {
break;
}
variable("tt", receive.toString());
echo("${tt}");
}
#Configuration
public class FtpConfig {
#Bean
public DefaultFtpSessionFactory ftpSessionFactory() {
DefaultFtpSessionFactory ftpSessionFactory = new DefaultFtpSessionFactory();
ftpSessionFactory.setHost("localhost");
ftpSessionFactory.setPort(21);
ftpSessionFactory.setUsername("user");
ftpSessionFactory.setPassword("pass");
return ftpSessionFactory;
}
}
#Bean
public ApplicationRunner runner(DefaultFtpSessionFactory sf) {
return args -> {
FTPFile[] list = sf.getSession().list(".");
for (FTPFile file: list ) {
System.out.println("Result: " + file.getName());
}
};
}
I have updated my libraries, and now e-mails are sent without subject. I don't know where this happened...
Mail API is 1.4.3., Spring 2.5.6. and Spring Integration Mail 1.0.3.RELEASE.
<!-- Definitions for SMTP server -->
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="${mail.host}" />
<property name="username" value="${mail.username}" />
<property name="password" value="${mail.password}" />
</bean>
<bean id="adminMailTemplate" class="org.springframework.mail.SimpleMailMessage" >
<property name="from" value="${mail.admin.from}" />
<property name="to" value="${mail.admin.to}" />
<property name="cc">
<list>
<value>${mail.admin.cc1}</value>
</list>
</property>
</bean>
<!-- Mail service definition -->
<bean id="mailService" class="net.bbb.core.service.impl.MailServiceImpl">
<property name="sender" ref="mailSender"/>
<property name="mail" ref="adminMailTemplate"/>
</bean>
And properties mail.host,mail.username,mail.password,mail.admin.from,mail.admin.to,
mail.admin.cc1.
Java class:
/** The sender. */
private MailSender sender;
/** The mail. */
private SimpleMailMessage mail;
public void sendMail() {
this.mail.setSubject("Subject");
this.mail.setText("msg body");
try {
getSender().send(this.mail);
} catch (MailException e) {
log.error("Error sending mail!",e);
}
}
public SimpleMailMessage getMail() {
return this.mail;
}
public void setMail(SimpleMailMessage mail) {
this.mail = mail;
}
public MailSender getSender() {
return this.sender;
}
public void setSender(MailSender mailSender1) {
this.sender = mailSender1;
}
Everything worked before, I am wondering if there may be any conflicts with new libraries.
Finally - I had the time to resolve this.
In pom.xml, I have added java mail dependency and remove exclusion for geronimo javamail in apache axis transport http dependency.
I expect it's something to do with the way that you're injecting a singleton SimpleMailMessage into your bean. This is not thread-safe, since every call to your sendMail method will be using the same underlying SimpleMailmessage object. It's quite possible that some implementation change in the new libraries now means this is broken.
SimpleMailMessage has a copy constructor, so you should do it like this:
<bean id="mailService" class="net.bbb.core.service.impl.MailServiceImpl">
<property name="sender" ref="mailSender"/>
<property name="template" ref="adminMailTemplate"/>
</bean>
and
private SimpleMailMessage template;
public void setTemplate(SimpleMailMessage template) {
this.template = template;
}
public void sendMail() {
SimpleMailMessage message = new SimpleMailMessage(template);
message.setSubject("Subject");
message.setText("msg body");
try {
getSender().send(message);
} catch (MailException e) {
log.error("Error sending mail!",e);
}
}