The below code is using Spring Integration 3.0.1
Client side integration XML:
<int:channel id="serviceTWeb"></int:channel>
<int:gateway id="serviceTGW" default-request-channel="serviceTWeb"
service-interface="com.test.ServiceTWeb">
</int:gateway>
<int-http:outbound-gateway
url="http://testserver:8080/service-webapp/service"
http-method="POST" id="RequestTNHTTP" reply-timeout="2000"
request-channel="serviceTWeb" message-converters="conv>
</int-http:outbound-gateway>
<bean id="conv" class="org.springframework.integration.http.converter.SerializingHttpMessageConverter">
</bean>
Web side integration XML:
<!-- The following uses a ServiceActivator on service -->
<bean id="stweb" class="test.poc.si.ServiceTWeb"></bean>
<bean id="conv" class="org.springframework.integration.http.converter.SerializingHttpMessageConverter">
</bean>
<int:channel id="requestChannel"></int:channel>
<int:channel id="replyChannel"></int:channel>
<int:service-activator input-channel="requestChannel" ref="stweb"
method="service" requires-reply="true" id="webserv"
output-channel="replyChannel">
</int:service-activator>
<int-http:inbound-gateway request-channel="requestChannel"
supported-methods="POST" path="/service" message-converters="conv"
reply-channel="replyChannel">
</int-http:inbound-gateway>
The client makes the request out to the server, the server side get the code
and processes the Request object, but the server tosses the following when sending the
reply message:
SEVERE: Servlet.service() for servlet [Multipart] in context with path [/service-webapp] threw exception [Request processing failed; nested exception is org.springframework.integration.MessagingException: Could not convert reply: no suitable HttpMessageConverter found for type [com.myobject.MReply] and accept types [[text/html, image/gif, image/jpeg, /;q=.2, /;q=.2]]] with root cause
org.springframework.integration.MessagingException: Could not convert reply: no suitable HttpMessageConverter found for type [com.myobject.MReply] and accept types [[text/html, image/gif, image/jpeg, /;q=.2, /;q=.2]
Any help would be welcome!
Assuming com.myobject.MReply is Serializable, try setting expected-response-type="com.myobject.MReply" on the outbound gateway. It should cause the accept header to be set to application/x-java-serialized-object.
EDIT:
Or, set expected-response-type="java.io.Serializable" if you don't want to tie it to a specific type.
Related
I am working on the spring integration http. Wanted to make multiple http calls and collect the response to one common java object. I am facing an issues which says no output/reply channel at the end of the aggregator.
This is the xml definition
<int:channel id="intermediateWChannel">
<int:interceptors>
<int:wire-tap channel="intermediateWLogger" />
</int:interceptors>
</int:channel>
<int:channel id="intermediateSChannel">
<int:interceptors>
<int:wire-tap channel="intermediateSLogger" />
</int:interceptors>
</int:channel>
<int:logging-channel-adapter id="intermediateWLogger" expression="'Fetch Wtms Trip : '.concat(payload)" level="INFO" />
<int:logging-channel-adapter id="intermediateSLogger" level="INFO" />
<int:chain input-channel="intermediateSChannel" output-channel="publishsubscribechannel">
<int-http:outbound-gateway
id="outboundGateway"
url="{url2}"
http-method="GET"
request-factory="requestFactory"
charset="UTF-8"
mapped-request-headers="Accept:application/json"
expected-response-type="java.lang.String"></int-http:outbound-gateway>
<int:object-to-json-transformer/>
</int:chain>
<int:chain id="chain2" input-channel="publishsubscribechannel" output-channel="aggregatorChannel">
<int:transformer ref="fetchTransformer" method="process" />
</int:chain>
<int:chain id="request-chain" input-channel="publishsubscribechannel" output-channel="aggregatorChannel">
<int-http:outbound-gateway
id="strideOutboundGateway1"
url="url3"
http-method="GET"
request-factory="requestFactory"
charset="UTF-8"
mapped-request-headers="Accept:application/json"
expected-response-type="java.lang.String"></int-http:outbound-gateway>
<int:transformer ref="fetchTransformer" method="process1" />
</int:chain>
<int:chain id="chain3" input-channel="aggregatorChannel" output-channel="outputChannel">
<int:aggregator id="tAggregator"
ref="tDataAggregator"
method="processAggregator"
correlation-strategy-expression="headers['id']"
release-strategy="aggregatorReleaseStrategy"
expire-groups-upon-completion="true"/>
</int:chain>
But, I have specified the output channel in the aggregator.
Below is the exception and even if I add the reply-channel in headers, application is going for stackoverflow.
org.springframework.messaging.core.DestinationResolutionException: no output-channel or replyChannel header available
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:452) ~[spring-integration-core-5.5.13.jar:5.5.13]
at org.springframework.integration.handler.AbstractMessageProducingHandler.doProduceOutput(AbstractMessageProducingHandler.java:325) ~[spring-integration-core-5.5.13.jar:5.5.13]
at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:268) ~[spring-integration-core-5.5.13.jar:5.5.13]
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutputs(AbstractMessageProducingHandler.java:232) ~[spring-integration-core-5.5.13.jar:5.5.13]
The transformer is different from other endpoints that it does not modify a Message we return from its method call. It is counted as transformer has a full control over the reply message creation. So, if there is a need to preserve some request message headers, it is recommended to use a MessageBuilder.copyHeadersIfAbsent() API. This way in request-reply scenario, the necessary replyChannel header is present in the reply message.
We probably have to mention shouldCopyRequestHeaders() behavior for the transformer in docs: https://docs.spring.io/spring-integration/docs/current/reference/html/message-transformation.html#messaging-transformation-chapter. Feel free to raise a GH issue.
Currently we are using Spring Integration 2.1.0 Release in our application.
Application flow is as below:
Some operation is performed in application and we got the output string in String via Active MQ.
I have used message-driven-channel-adapter and service-activator to read the data from queue.
That data is displayed successfully on Server(application is working as client) using tcp-outbound-gateway.
Problem is while getting the acknowledgement from server.
Created a new channel and entered in reply-channel in tcp-outbound-gateway
Passing the same channel in service-activator as input channel.
It is showing below error:
[task-scheduler-5] 2017-10-05 18:32:20,732 ERROR org.springframework.integration.handler.LoggingHandler - org.springframework.integration.MessageDeliveryException: Dispatcher has no subscribers.
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:108)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:101)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:61)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:157)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:128)
at org.springframework.integration.core.MessagingTemplate.doSend(MessagingTemplate.java:288)
at org.springframework.integration.core.MessagingTemplate.send(MessagingTemplate.java:149)
Code is as below
<context:property-placeholder />
<!-- Gateway and connection factory setting -->
<int:channel id="telnetLandingChannel" />
<int:channel id="telnetReplyChannel" />
<beans:bean id="clientCustomSerializer"
class="com.telnet.core.serializer.CustomSerializer">
<beans:property name="terminatingChar" value="10" />
<beans:property name="maxLength" value="65535" />
</beans:bean>
<int:gateway id="gw" default-reply-channel="telnetReplyChannel" default-reply-timeout="100000"
service-interface="com.telnet.core.integration.connection.ParseTcpConfiguration$SimpleGateway"
default-request-channel="telnetLandingChannel"/>
<ip:tcp-connection-factory id="clientFactory"
type="client" host="localhost" port="7777" single-use="false" using-nio="false"
serializer="${client.serializer}" deserializer="${client.serializer}" />
<ip:tcp-outbound-gateway id="clientInGw"
request-channel="telnetLandingChannel"
connection-factory="clientFactory"
reply-channel="telnetReplyChannel"
reply-timeout="100000"/>
<!-- To send the messege over server via JMS and serviceActivator -->
<int:channel id="incidentDispatchMessageChannel" />
<int:channel id="jmsChannel" />
<beans:bean id="customClientServiceActivator"
class= "com.telnet.core.integration.CustomClientServiceActivator">
</beans:bean>
<int-jms:message-driven-channel-adapter id="incidentDispatchMessageChannelAdapter" error-channel="errorChannel"
connection-factory="mqConnectionFactory"
destination-name="${incident.processing.messaging.dispatch.queues}"
channel="incidentDispatchMessageChannel"/>
<int:service-activator id="incidentMessageActivator"
input-channel="incidentDispatchMessageChannel"
output-channel="jmsChannel"
ref="customClientServiceActivator" method="getOutboundMessage">
</int:service-activator>
<int:object-to-string-transformer id="clientBytes2String"
input-channel="jmsChannel"
output-channel="telnetLandingChannel"/>
<!-- To receive the acknowledgement message on server via serviceActivator -->
<int:service-activator id="incidentAck"
input-channel="telnetReplyChannel"
ref="customClientServiceActivator" method="getAck">
</int:service-activator>
I have studied various article on stackverFlow but not able to get any solution
Yeah... That isn't clear by the error what channel is guilty.
On the other hand you really use very old Spring Integration version.
Would be great to consider to upgrade to the latest: http://projects.spring.io/spring-integration/.
However I think that issue is somehow around exactly that reply-channel, which you use not only for the <service-activator> but for the <int:gateway> as well.
I suggest you to remove default-reply-channel="telnetReplyChannel" from the gateway definition, remove reply-channel="telnetReplyChannel" from the <ip:tcp-outbound-gateway> definition. And let them communicate via replyChannel header populated by the gateway during request.
Regarding your <int-jms:message-driven-channel-adapter> flow which leads to the same <ip:tcp-outbound-gateway>, I would suggest to still stay with the replyChannel header but here populate it via <header-enricher> before sending message to the telnetLandingChannel. That replyChannel via <header-enricher> would be exactly an input-channel for the subsequent <int:service-activator> to handle ack from the <ip:tcp-outbound-gateway>.
I got the solution of this issue, there are multiple xmls in our code but i have added the code in one to show flow in stackOverflow.
Issue was i had defined in a xml which has only configuration part like Outbound adapter connection factory where as it should be defined in another xml where I am using service activator. Changed the place of channel definition and it worked.
I want to disconnect the TCP (as server) the moment i got the response message. As of now I am using so-timeout, so my TCP server will gets timedout after the time given in so-timeout, but requirement is to disconnect the connection the moment TCP print/display the acknowledgement. Please suggest how can I implement this.
I am using spring integration (4.0.6) to make SOAP calls from rest service using int-http:outbound-gateway and int-ws:outbound-gateway. Is there a way to log SOAP request message only in case of exception.
<int:gateway id="myGateway" service-interface="myservice">
<int:method name="getData" request-channel="reqChannel" reply-channel="repChannel">
</int-gateway>
my outbound-gateway configured as below
<int-http:outbound-gateway id="og1" request-channel="reqChannel" url="xxx" http-method="POST" reply-channel="repChannel" reply-timeout="10000" message-converters="converter" request-factory="reqFactory">
<bean id="reqFactory"
class="org.springframework.http.client.SimpleClientHttpRequestFactory">
<property name="cTimeout" value="20000"/>
<property name="rTimeout" value="20000"/>
</bean>
Ok, Try this method.
Create your own error Handler which implements the spring Error Handler Interface. In your context file
<bean id="soapErrorHandlingSyncTaskExecutor"
class="org.springframework.integration.util.ErrorHandlingTaskExecutor">
<constructor-arg>
<bean class="org.springframework.core.task.SyncTaskExecutor" />
</constructor-arg>
<constructor-arg>
<bean class="ErrorHandle"
p:errorSender-ref="errorSender"/>
</constructor-arg>
</bean>
<bean id="errorSender" class="org.springframework.integration.core.MessagingTemplate"
p:defaultChannel-ref="errors" />
When ever your WS adapter throws any exception then org.springframework.integration.util.ErrorHandlingTaskExecutor catches this exception now its upto you how you handle the exception in your error handler.
The SOAP faultcode and faultstring is found in the MessagingException's cause (if a SoapFaultClientException).
Here i am sending the error messages to a channel for me to monitor.
Hope this helps.
Would be better to share some config and the point where would you like to get a log/exception.
Both, <int-http:outbound-gateway> and <int-ws:outbound-gateway> have <request-handler-advice-chain> ability, where you could use ExpressionEvaluatingRequestHandlerAdvice and send an ErrorMessage with the request (guilty?) message for any diagnostics.
You should configure the readTimeout on the appropriate ClientHttpRequestFactory, e.g. HttpComponentsClientHttpRequestFactory#setReadTimeout(int timeout).
Regarding converters and the wish to log their results in case of error...
How about to implement ClientHttpRequestInterceptor with the desired logic:
try {
return execution.execute(request, body);
catch (Exception e) {
this.errorChannel.send(...);
}
I'm prety sure that the byte[] body is your SOAP request.
See ClientHttpRequestInterceptor JavaDocs for more info.
The ClientHttpRequestInterceptor is part of RestTemplate, which can be injected to the http:outbound-gateway.
For the ws:outbound-gateway you should use ClientInterceptor with the handleFault implementation and extract MessageContext.getRequest() for your purpose.
i have two int-http:inbound-gateway with path as mentioned below.And points to same request channel but have different reply-channel.
http://localhost:8080/XYZ/ABCService/query -- i expected to call http:inbound-gateway with id ="XYZ"
http://localhost:8080/ABCService/query - i expected to call http:inbound-gateway with id ="default"
but what happing in its not consistence when i give request to
http://localhost:8080/XYZ/ABCService/query
it is calling "default" gateway other time its calling "XYZ" i.e not consistence. or not sure it may call correctly but instead gives response to different reply-channel ?
I am using DispatcherServlet.Below my spring-integration.xml
<int-http:inbound-gateway id="default"
path="/*Service/query"
request-channel="RequestChannel" reply-channel="ResponseChannel"
supported-methods="POST" reply-timeout="5000" request-payload-type="java.lang.String"
error-channel="ErrorChannel" mapped-request-headers="xyz-*, HTTP_REQUEST_HEADERS">
<int-http:header name="reply-type" expression="'DEFAULT'" />
</int-http:inbound-gateway>
<int-http:inbound-gateway id="XYZ"
path="/XYZ/*Service/query"
request-channel="RequestChannel" reply-channel="XYZExportTransformedChannel"
supported-methods="POST" reply-timeout="5000" request-payload-type="java.lang.String"
error-channel="ErrorChannel" mapped-request-headers="xyz-*, HTTP_REQUEST_HEADERS">
<int-http:header name="reply-type" expression="'ABC'" />
</int-http:inbound-gateway>
<!--All endpoints output chanlle is CommonResonseChannel -->
<int:channel id="CommonResponseChannel">
</int:channel>
<!-- final router -->
<int:header-value-router input-channel="CommonResponseChannel"
header-name="reply-type">
<int:mapping value="DEFAULT" channel="ResponseChannel" />
<int:mapping value="ABC" channel="XYZResponseChannel" />
</int:header-value-router>
<int:channel id="ResponseChannel">
</int:channel>
<int:channel id="XYZResponseChannel">
</int:channel>
<int:transformer input-channel="XYZResponseChannel"
output-channel="XYZExportTransformedChannel" id="TransformerChannel"
ref="objToCSVTransformer"></int:transformer>
<bean class="SomeTransformer"
id="objToCSVTransformer"></bean>
<int:channel id="XYZExportTransformedChannel" />
I have opened this question before not not very clear.Not sure how to update that.So opened new one.
You should not configure your reply channels that way; it may cause unpredictable results.
reply-channels are simply bridged to the message replyChannel header so it generally will work ok, but it's unpredictable because the bridge is set up when the gateway first receives a message.
Instead, simply omit the reply-channel attributes on the gateways and let the framework route the replies directly using the replyChannel header.
Instead of your router, configure a "bridge to nowhere" (a <bridge/> with input-channel= CommonResponseChannel" and no output-channel.
Or simply omit the output-channel on the last endpoints (instead of sending to CommonResponseChannel).
I am facing some chellenge while setting the timeout for the soap request to ESB interface from my interface(Using spring integration). While sending the request to ESB interface, I am setting the request timeout = 5000ms and reply timeout = 5000ms but in case the services is down on ESB interface, the request is not timeout in the desired timeout time of 5000ms(5 sec) and timeout sometimes in 40sec or more. I tried to use the default request timeout and default reply timeout options also with the int:gateway configuration but the same issue. Please see the below configuration done:
<int:gateway id="SoapESBGateway"
service-interface="test.soap.service.ServiceSoap">
<int:method name="ServiceResponse"
request-channel="RequestChannel" reply-channel="ReplyChannel"
request-timeout="5000" reply-timeout="5000" />
</int:gateway>
<int:chain input-channel="RequestChannel" output-channel="ReplyChannel">
<int-xml:marshalling-transformer marshaller="marshaller" result-transformer="resultTransformer"></int-xml:marshalling-transformer>
<int:transformer ref="TransformerBean" method="transformMethod"></int:transformer>
<int-ws:outbound-gateway id="ws-SoapESB-gateway" ignore-empty-responses="true" uri="${soap.URI}"></int-ws:outbound-gateway>
<int:transformer ref="TransformerBean" method="transformMethod"></int:transformer>
<int-xml:unmarshalling-transformer unmarshaller="marshaller"></int-xml:unmarshalling-transformer>
</int:chain>
<bean id="resultTransformer" class="org.springframework.integration.xml.transformer.ResultToStringTransformer" />
<bean id="TransformerBean" class="test.TransformerImpl"></bean>
Please let me know if I am missing something while setting the timeout option.
Thanks,
Vinay A
updated:
<int:gateway id="SoapESBGateway"
service-interface="test.soap.service.ServiceSoap">
<int:method name="ServiceResponse"
request-channel="RequestChannel" reply-timeout="5000" />
</int:gateway>
<int:chain input-channel="RequestChannel">
<int-xml:marshalling-transformer marshaller="marshaller" result-transformer="resultTransformer"></int-xml:marshalling-transformer>
<int:transformer ref="TransformerBean" method="transformMethod"></int:transformer>
<int-ws:outbound-gateway id="ws-SoapESB-gateway" ignore-empty-responses="true" uri="${soap.URI}" message-sender="messageSender"></int-ws:outbound-gateway>
<int:transformer ref="TransformerBean" method="transformMethod"></int:transformer>
<int-xml:unmarshalling-transformer unmarshaller="marshaller"></int-xml:unmarshalling-transformer>
</int:chain>
<bean id="resultTransformer" class="org.springframework.integration.xml.transformer.ResultToStringTransformer" />
<bean id="TransformerBean" class="test.TransformerImpl"></bean>
<bean id ="messageSender" class="org.springframework.ws.transport.http.HttpComponentsMessageSender">
<property name="connectionTimeout" value="2000"></property>
</bean>
As above, I have added messageSender bean reference with the int-ws:outbound-gateway and set the property connectiontimeout = 2sec but I can still see that this is waiting around for 19sec for first hit when network is down and for the next hits, less then 19 sec but not the desired timeout for the first hit at least. So do we not have any option where we may ascertain that the desired timeout work always.
Thanks,
Vinay
First of all, please, read this chapter: http://docs.spring.io/spring-integration/docs/latest-ga/reference/html/messaging-endpoints-chapter.html#d4e4197
According to your config you have direct flow, so there is no reason to have request-timeout and reply-channel.
The issue of your 40sec is under that WS gateway's WebServiceMessageSender. In your case it is HttpUrlConnectionMessageSender by default. And there is some connectTimeout attribute in the HttpURLConnection.
I'm not sure how we can change it, but I prefer to use HttpComponentsMessageSender to have more control over HTTP connections.
Now the question Why is it 40sec, but not those 5sec from reply-timeout?. Because your thread is blocked on the send and it hasn't reached to the wait reply part yet.