Use Java object in mule - groovy

I am using a java object which should return me an endpoint, then I want to invoke a service hosted at the specified endpoint. Please assist.
Below is my effort
In mule.xml
<spring:beans>
<spring:bean id="reqUrl" class="com.mule.sbus.drools.RequestUrl"
scope="singleton" />
</spring:beans>
<bpm:drools />
<http:listener-config name="NorthboundSingleEntrypoint"
host="0.0.0.0" port="8191" doc:name="HTTP Listener Configuration" />
<http:request-config name="HTTP_Request_Configuration"
host="acdc3a38cffc411e5a18606a62b4ee07-877599714.us-west-1.elb.amazonaws.com"
port="80" doc:name="HTTP Request Configuration" />
<flow name="sbusdroolsFlow">
<http:listener config-ref="NorthboundSingleEntrypoint"
path="/*" doc:name="HTTP" />
<set-variable variableName="requestUrl"
value="#[message.inboundProperties.'http.request.path']" doc:name="RequestUrl" />
<script:component doc:name="Script">
<script:script engine="groovy">
<![CDATA[
return requestUrl;
]]>
</script:script>
</script:component>
<bpm:rules rulesDefinition="routingRules.drl"
initialFacts-ref="reqUrl" />
<expression-transformer evaluator="groovy"
expression="message.getPayload().getObject()" doc:name="Expression" />
<logger message="#[groovy:message.getPayload().getObject()]" level="INFO"
doc:name="LoggerResp" />
</flow>
Below is my drools .drl
#default dialect for the semantic code will be MVEL
global org.mule.module.bpm.MessageService mule;
import com.mule.sbus.drools.RequestUrl
dialect "mvel"
declare RequestUrl
#role(event)
end
rule "test123"
lock-on-active
when
$url:RequestUrl(url=="test123")
then
#order.setDestination("WAREHOUSE_A");
modify($url){setEndPoint("test123")}
end
rule "test234"
lock-on-active
when
$url:RequestUrl(url=="test234")
then
#order.setDestination("WAREHOUSE_A");
modify($url){setEndPoint("test234")}
end
and my java class
package com.mule.sbus.drools;
public class RequestUrl {
private String url;
private String endPoint;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getEndPoint() {
return endPoint;
}
public void setEndPoint(String endPoint) {
/*if(endPoint=="test123")
this.endPoint = endPoint;
else*/
this.endPoint = "/checkcibil";
System.out.println("inside java :::: " + endPoint);
}
#Override
public String toString() {
// TODO Auto-generated method stub
return "url : " + url + " endPoint : " + endPoint;
}
}
As you can see I am invoking my setter from the drools file and once I get the string I want to print the same using
<logger message="#[groovy:message.getPayload().getObject()]" level="INFO"
doc:name="LoggerResp" />
but I don't know what should be the message value to use. Please assist

Got the answer,
As I am using groovy, have commented drools, and the updated the code as below
<script:component doc:name="Script">
<script:script engine="groovy">
<![CDATA[
reqUrl.setEndPoint(requestUrl);
String endpnt = reqUrl.getEndPoint();
message.setProperty('endpnt', endpnt,org.mule.api.transport.PropertyScope.INVOCATION);
]]>
</script:script>
</script:component>
<logger message="#[flowVars['endpnt']]" level="INFO" doc:name="LoggerResp" />
Using groovy I am invoking the setter and the call the getter to have the value in endpt variable. This can now be set as a property in the message and later we can retrieve the same (outside groovy script tags), using #[flowVars['endpnt']]

Related

Custom PatternLayoutConverter with log4net.Ext.Json?

I have the following log4net configuration:
<log4net>
<appender name="Console" type="log4net.Appender.ConsoleAppender">
<layout type='log4net.Layout.SerializedLayout, log4net.Ext.Json'>
<renderer type='log4net.ObjectRenderer.JsonDotNetRenderer, log4net.Ext.Json.Net'>
<DateFormatHandling value="IsoDateFormat" />
<NullValueHandling value="Ignore" />
</renderer>
<converter>
<name value="preparedMessage" />
<type value="JsonLogs.CustomLayoutConverter" />
</converter>
<default />
<remove value='message' />
<remove value='ndc' />
<member value='message:messageObject' />
<member value='details:preparedMessage' />
</layout>
</appender>
<appender name="Console2" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<converter>
<name value="preparedMessage" />
<type value="JsonLogs.CustomLayoutConverter" />
</converter>
<conversionPattern value="%level %thread %logger - %preparedMessage%newline" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="Console" />
<appender-ref ref="Console2" />
</root>
</log4net>
with the following implementation of my custom PatternLayoutConverter:
namespace JsonLogs
{
using System.IO;
using log4net.Core;
using log4net.Layout.Pattern;
public class CustomLayoutConverter : PatternLayoutConverter
{
#region Methods
protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
{
if (loggingEvent.MessageObject is string stringMessage)
{
writer.Write(new { message = stringMessage });
}
else
{
writer.Write(loggingEvent.RenderedMessage);
}
}
#endregion
}
}
For some reason, the converter works perfectly fine with the Console2 appender(which is not JSON driven) but it doesn't work with the Console appender whose output is JSON.
Example of the output:
Console -> {"date":"2018-12-09T12:25:28.0529041+03:00","level":"INFO","appname":"JsonLogs.exe","logger":"JsonLogs.Program","thread":"1","message":"Test","details":"preparedMessage"}
Console2 -> INFO 1 JsonLogs.Program - { message = Test }
My goal is to have details always in JSON that's why I introduced my own converter to catch primitive values and wrap them in a custom object.
Is my configuration wrong? Or I'm missing something? Could you help me, please, to figure this out?
Thank you
The issue seems to be a bug of log4net.Ext.Json. I'm going to report it on their GitLab.
So far, I ended up with my custom log4net layout which looks like this
public class CustomLayout : PatternLayout
{
#region Public Methods and Operators
public override void Format(TextWriter writer, LoggingEvent loggingEvent)
{
var message = loggingEvent.MessageObject.GetType().IsPrimitive || loggingEvent.MessageObject is string || loggingEvent.MessageObject is decimal || loggingEvent.MessageObject is BigInteger
? new { message = loggingEvent.MessageObject }
: loggingEvent.MessageObject;
writer.WriteLine(JsonConvert.SerializeObject(new
{
timestamp = loggingEvent.TimeStampUtc,
threadId = loggingEvent.ThreadName,
details = message,
logger = loggingEvent.LoggerName,
level = loggingEvent.Level.DisplayName,
user = loggingEvent.UserName
}));
}
#endregion
}
it meets my needs and does exactly what I want.
The exact place of this problem is AddMember Method and its implementation. Here is SerializedLayout source code for that:
public virtual void AddMember(string value)
{
var arrangement = log4net.Util.TypeConverters.ArrangementConverter.GetArrangement(value, new ConverterInfo[0]);
m_arrangement.AddArrangement(arrangement);
}
As you can see the second parameter of GetArrangment is empty array of ConverterInfo, Though there must be our custom attached ones (by AddConverter method or by xml).
As the solution you can implement your own subclass that will derive from SerializedLayout with overridden AddMember like this:
public override void AddMember(string value)
{
var customConverter = new ConverterInfo("lookup", typeof(CustomPatternConverter));
var arrangement = log4net.Util.TypeConverters.ArrangementConverter.GetArrangement(value, new ConverterInfo[] { customConverter });
m_arrangement.AddArrangement(arrangement);
}
Hope it helps as it did with my case!

Injecting 2 different rest templates in spring integration/xd http-outbound

Hi I have stream definition like below.Where i pull file from s3 split line by line and call http client and put to named channel.My transport is rabbit and
stream aws-s3|custom processor| custom-http-client --url1=https://test1.com --url2=https://test2.com --filterAttribute=messageAttribute --httpMethod=POST --nonRetryErrorCodes=400,401,404,500 --charset=UTF-8 --replyTimeout=30000 --mapHeaders=Api-Key,Content-Type --requestTimeOut=30000 |processor> queue:testQueue
my http-config looks like below and using apache http client for connection pooling and multithreaded I am putting back to DLQ all very errors like socket time out and retrying it .
Now my uri1 uses Oauth and uri2 uses basic restTemplate. How can i inject two rest template to my http-outbound? One will be oauth and other will be basicTemplate?
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:int-http="http://www.springframework.org/schema/integration/http"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/http
http://www.springframework.org/schema/integration/http/spring-integration-http.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- <context:property-placeholder location="${xd.module.config.location}\processor\${xd.module.name}\batch-http.properties"
ignore-resource-not-found="true" local-override="true"/> -->
<context:property-placeholder />
<!-- logger changes start -->
<channel-interceptor pattern="*" order="3">
<beans:bean class="org.springframework.integration.channel.interceptor.WireTap">
<beans:constructor-arg ref="loggingChannel" />
</beans:bean>
</channel-interceptor>
<logging-channel-adapter id="loggingChannel" log-full-message="true" level="ERROR"/>
<!-- logger changes end -->
<header-filter input-channel="input"
output-channel="inputX" header-names="x-death"/>
<service-activator input-channel="inputX" ref="gw" />
<gateway id="gw" default-request-channel="toHttp" default-reply-timeout="0" error-channel="errors" />
<beans:bean id="inputfields" class="test.HTTPInputProperties">
<beans:property name="nonRetryErrorCodes" value="${nonRetryErrorCodes}"/>
</beans:bean>
<beans:bean id="responseInterceptor" class="test.ResponseInterceptor">
<beans:property name="inputProperties" ref="inputfields" />
</beans:bean>
<chain input-channel="errors" output-channel="output">
<!-- examine payload.cause (http status code etc) and decide whether
to throw an exception or return the status code for sending to output -->
<header-filter header-names="replyChannel, errorChannel" />
<transformer ref="responseInterceptor" />
</chain>
<int-http:outbound-gateway id='batch-http' header-mapper="headerMapper"
request-channel='toHttp'
rest-template="batchRestTemplate"
url-expression="payload.contains('${filterAttribute}') ? '${url1}' : '${url2}'" http-method="${httpMethod}"
expected-response-type='java.lang.String' charset='${charset}'
reply-timeout='${replyTimeout}' reply-channel='output'>
</int-http:outbound-gateway>
<beans:bean id="batchHTTPConverter" class="org.springframework.http.converter.StringHttpMessageConverter" >
<beans:constructor-arg index="0" value="${charset}"/>
<beans:property name="supportedMediaTypes" value = "application/json;UTF-8" />
</beans:bean>
<beans:bean id="batchRestTemplate" class="testBatchRestTemplate" >
<beans:constructor-arg name="requestTimeOut" value="${requestTimeOut}"/>
<beans:constructor-arg name="maxConnectionPerRoute" value="${maxConnectionPerRoute}"/>
<beans:constructor-arg name="totalMaxConnections" ref="${totalMaxConnections}"/>
</beans:bean>
<beans:bean id="headerMapper" class="org.springframework.integration.http.support.DefaultHttpHeaderMapper"
factory-method="outboundMapper">
<beans:property name="outboundHeaderNames" value="${mapHeaders}"/>
<beans:property name="userDefinedHeaderPrefix" value=""/>
</beans:bean>
<channel id="output" />
<channel id="input" />
<channel id="inputX" />
<channel id="toHttp" />
</beans:beans>
public class BatchRestTemplate extends RestTemplate{
private static final Logger LOGGER = LoggerFactory
.getLogger(BatchRestTemplate.class);
private static Integer requestTimeOut;
private static Integer totalMaxConnections;
private static Integer maxConnectionPerRoute;
public BatchRestTemplate(Integer requestTimeOut,Integer totalMaxConnections,Integer maxConnectionPerRoute) throws NoSuchAlgorithmException {
super(createBatchHttpRequestFactory());
List<HttpMessageConverter<?>> messageConverters= new ArrayList<HttpMessageConverter<?>>();
messageConverters.addAll(getMessageConverters());
messageConverters.add(0,new StringHttpMessageConverter(Charset.forName("UTF-8")));
setMessageConverters(messageConverters);
}
private static ClientHttpRequestFactory createBatchHttpRequestFactory() throws NoSuchAlgorithmException {
CloseableHttpClient httpClient;
HttpComponentsClientHttpRequestFactory httpRequestFactory;
SSLConnectionSocketFactory socketFactory;
socketFactory = new SSLConnectionSocketFactory(
SSLContext.getDefault(),
new String[] {"TLSv1"},
null,
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", socketFactory)
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
cm.setMaxTotal(250);
cm.setDefaultMaxPerRoute(100);
cm.closeExpiredConnections();
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(30000)
.setConnectionRequestTimeout(30000).setSocketTimeout(30000).build();
httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).setConnectionManager(cm).build();
httpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
return httpRequestFactory;
}
}
ResponseInterceptor
public class ResponseInterceptor {
private HTTPInputProperties inputProperties;
private static final Logger LOGGER = LoggerFactory.getLogger(ResponseInterceptor.class);
/**
* Intercepts the errorMessage from the API response and sends appropriate
* information to the Output channel.
*
* #param errorMessage
* #return Message
*/
public Message<String> transform(Message<MessagingException> errorMessage) {
LOGGER.error("Inside Response Interceptor !");
Message<String> responseMessage = null;
try {
if (null != errorMessage && null != errorMessage.getPayload()
&& null != errorMessage.getPayload().getCause()) {
LOGGER.error("Cause is - " + errorMessage.getPayload().getCause().getMessage());
if (errorMessage.getPayload().getCause() instanceof HttpClientErrorException) {
HttpClientErrorException clientError = (HttpClientErrorException) errorMessage.getPayload()
.getCause();
LOGGER.error("Error in ResponseInceptor", clientError);
List<String> errorCodeList = getErrorCodes(inputProperties.getNonRetryErrorCodes());
// intercept Only those errors that are defined as
// nonRetryErrorCodes options in stream definition
if (null != clientError.getStatusCode()
&& errorCodeList.contains(clientError.getStatusCode().toString())) {
LOGGER.error("Error in Response Body", clientError.getResponseBodyAsString());
LOGGER.debug("Non retry message found. Sending to output channel without retrying");
responseMessage = MessageBuilder.withPayload((null == clientError.getResponseBodyAsString() || clientError.getResponseBodyAsString().isEmpty())
? getDefaultPayload(clientError.getStatusCode().toString()) : clientError.getResponseBodyAsString())
.setHeader(BatchHttpClientConstants.HTTP_STATUS, clientError.getStatusCode().toString())
.setHeader(BatchHttpClientConstants.REQUEST_OBJECT,
getFailedMessagePayload(errorMessage))
.copyHeaders(errorMessage.getPayload().getFailedMessage().getHeaders())
.setReplyChannelName(BatchHttpClientConstants.OUTPUT).setErrorChannelName(null).build();
} else {
LOGGER.debug("Status code from API is not present in the nonRetryCodes");
}
} else if (errorMessage.getPayload().getCause() instanceof HttpServerErrorException) {
LOGGER.error("Error is Instance of HttpServerErrorException");
HttpServerErrorException serverError = (HttpServerErrorException) errorMessage.getPayload()
.getCause();
responseMessage = MessageBuilder
.withPayload((null == serverError.getResponseBodyAsString()
|| serverError.getResponseBodyAsString().isEmpty())
? getDefaultPayload(serverError.getStatusCode().toString())
: serverError.getResponseBodyAsString())
.setHeader(BatchHttpClientConstants.HTTP_STATUS, serverError.getStatusCode().toString())
.setHeader(BatchHttpClientConstants.REQUEST_OBJECT, getFailedMessagePayload(errorMessage))
.copyHeaders(errorMessage.getPayload().getFailedMessage().getHeaders())
.setReplyChannelName(BatchHttpClientConstants.OUTPUT).setErrorChannelName(null).build();
}
}
} catch (Exception exception) {
LOGGER.error("Exception occured while transforming errorResponse", exception);
}
// returning null will send the message back to previous module
return responseMessage;
}
private String getDefaultPayload(String httpStatusCode) {
JSONObject jsonResponse = new JSONObject();
if (BatchHttpClientConstants.INTERNAL_SERVER_ERROR.equalsIgnoreCase(httpStatusCode)) {
jsonResponse.put(BatchHttpClientConstants.ID, BatchHttpClientConstants.INTERNAL_SERVER_ERROR_SUBCODE);
jsonResponse.put(BatchHttpClientConstants.TEXT, "Internal Server Error");
} else if (BatchHttpClientConstants.RESOURCE_NOT_FOUND.equalsIgnoreCase(httpStatusCode)) {
jsonResponse.put(BatchHttpClientConstants.ID, BatchHttpClientConstants.RESOURCE_NOT_FOUND_SUBCODE);
jsonResponse.put(BatchHttpClientConstants.TEXT, "Empty Response From the API");
}else{
jsonResponse.put(BatchHttpClientConstants.ID, BatchHttpClientConstants.GENERIC_ERROR_SUBCODE);
jsonResponse.put(BatchHttpClientConstants.TEXT, "Generic Error Occured.");
}
return jsonResponse.toString();
}
/**
* Get Individual error codes using delimiter
*
* #param nonRetryErrorCodes
* #return List of Error Codes as string
*/
private List<String> getErrorCodes(String nonRetryErrorCodes) {
List<String> errorCodeList = new ArrayList<String>();
StringTokenizer st = new StringTokenizer(nonRetryErrorCodes, BatchHttpClientConstants.DELIMITER);
while (st.hasMoreElements()) {
errorCodeList.add(st.nextToken());
}
return errorCodeList;
}
/**
* returns failed Message Payload
*
* #param errorMessage
* #return String
* #throws UnsupportedEncodingException
*/
private byte[] getFailedMessagePayload(Message<MessagingException> errorMessage)
throws UnsupportedEncodingException {
if (null != errorMessage.getPayload().getFailedMessage()
&& null != errorMessage.getPayload().getFailedMessage().getPayload()) {
return errorMessage.getPayload().getFailedMessage().getPayload().toString()
.getBytes(BatchHttpClientConstants.UTF_8);
}
return "".getBytes(BatchHttpClientConstants.UTF_8);
}
public HTTPInputProperties getInputProperties() {
return inputProperties;
}
public void setInputProperties(HTTPInputProperties inputProperties) {
this.inputProperties = inputProperties;
}
}
You can't - you need two gateways and a router (using the URL to route to one or the other).

losing messages when calling http call with high concurrency

Hi I have stream definition like below.Where i pull file from s3 split line by line and call http client and put to named channel.My transport is rabbit and prefetch is 10 and concurrency of http is 100 and running on 3 container and 1 admin.
stream aws-s3|custom processor| custom-http-client --url1=https://test1.com --url2=https://test1.com --filterAttribute=messageAttribute --httpMethod=POST --nonRetryErrorCodes=400,401,404,500 --charset=UTF-8 --replyTimeout=30000 --mapHeaders=Api-Key,Content-Type --requestTimeOut=30000 |processor> queue:testQueue
my http-config looks like below and using apache http client for connection pooling and multithreaded I am putting back to DLQ all very errors like socket time out and retrying it .All not retry error 50x i am passing to next module and writing to error queue.But after I call my external rest API i am losing messages.I am sending around 220 k messages some time i get 200k messages some time i get all 220k and some time 210k its random.Not sure if i am doing anything wrong.I tried to increase the request time out socket time out.Till my processor before HTTP i get all message but after http client i see lesser messages in my named channel queue and nothing in error queue. But i am pretty sure messages are getting lost after calling http-client .This happens when there is high load of data like million and 200k+ records for lesser load like 500 to 1000 records i don't see this issue.
<beans:beans xmlns="http://www.springframework.org/schema/integration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:int-http="http://www.springframework.org/schema/integration/http"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/http
http://www.springframework.org/schema/integration/http/spring-integration-http.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd">
<!-- <context:property-placeholder location="${xd.module.config.location}\processor\${xd.module.name}\batch-http.properties"
ignore-resource-not-found="true" local-override="true"/> -->
<context:property-placeholder />
<!-- logger changes start -->
<channel-interceptor pattern="*" order="3">
<beans:bean class="org.springframework.integration.channel.interceptor.WireTap">
<beans:constructor-arg ref="loggingChannel" />
</beans:bean>
</channel-interceptor>
<logging-channel-adapter id="loggingChannel" log-full-message="true" level="ERROR"/>
<!-- logger changes end -->
<header-filter input-channel="input"
output-channel="inputX" header-names="x-death"/>
<service-activator input-channel="inputX" ref="gw" />
<gateway id="gw" default-request-channel="toHttp" default-reply-timeout="0" error-channel="errors" />
<beans:bean id="inputfields" class="test.HTTPInputProperties">
<beans:property name="nonRetryErrorCodes" value="${nonRetryErrorCodes}"/>
</beans:bean>
<beans:bean id="responseInterceptor" class="test.ResponseInterceptor">
<beans:property name="inputProperties" ref="inputfields" />
</beans:bean>
<chain input-channel="errors" output-channel="output">
<!-- examine payload.cause (http status code etc) and decide whether
to throw an exception or return the status code for sending to output -->
<header-filter header-names="replyChannel, errorChannel" />
<transformer ref="responseInterceptor" />
</chain>
<int-http:outbound-gateway id='batch-http' header-mapper="headerMapper"
request-channel='toHttp'
rest-template="batchRestTemplate"
url-expression="payload.contains('${filterAttribute}') ? '${url1}' : '${url2}'" http-method="${httpMethod}"
expected-response-type='java.lang.String' charset='${charset}'
reply-timeout='${replyTimeout}' reply-channel='output'>
</int-http:outbound-gateway>
<beans:bean id="batchHTTPConverter" class="org.springframework.http.converter.StringHttpMessageConverter" >
<beans:constructor-arg index="0" value="${charset}"/>
<beans:property name="supportedMediaTypes" value = "application/json;UTF-8" />
</beans:bean>
<beans:bean id="batchRestTemplate" class="testBatchRestTemplate" >
<beans:constructor-arg name="requestTimeOut" value="${requestTimeOut}"/>
<beans:constructor-arg name="maxConnectionPerRoute" value="${maxConnectionPerRoute}"/>
<beans:constructor-arg name="totalMaxConnections" ref="${totalMaxConnections}"/>
</beans:bean>
<beans:bean id="headerMapper" class="org.springframework.integration.http.support.DefaultHttpHeaderMapper"
factory-method="outboundMapper">
<beans:property name="outboundHeaderNames" value="${mapHeaders}"/>
<beans:property name="userDefinedHeaderPrefix" value=""/>
</beans:bean>
<channel id="output" />
<channel id="input" />
<channel id="inputX" />
<channel id="toHttp" />
</beans:beans>
public class BatchRestTemplate extends RestTemplate{
private static final Logger LOGGER = LoggerFactory
.getLogger(BatchRestTemplate.class);
private static Integer requestTimeOut;
private static Integer totalMaxConnections;
private static Integer maxConnectionPerRoute;
public BatchRestTemplate(Integer requestTimeOut,Integer totalMaxConnections,Integer maxConnectionPerRoute) throws NoSuchAlgorithmException {
super(createBatchHttpRequestFactory());
List<HttpMessageConverter<?>> messageConverters= new ArrayList<HttpMessageConverter<?>>();
messageConverters.addAll(getMessageConverters());
messageConverters.add(0,new StringHttpMessageConverter(Charset.forName("UTF-8")));
setMessageConverters(messageConverters);
}
private static ClientHttpRequestFactory createBatchHttpRequestFactory() throws NoSuchAlgorithmException {
CloseableHttpClient httpClient;
HttpComponentsClientHttpRequestFactory httpRequestFactory;
SSLConnectionSocketFactory socketFactory;
socketFactory = new SSLConnectionSocketFactory(
SSLContext.getDefault(),
new String[] {"TLSv1"},
null,
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", socketFactory)
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
cm.setMaxTotal(250);
cm.setDefaultMaxPerRoute(100);
cm.closeExpiredConnections();
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(30000)
.setConnectionRequestTimeout(30000).setSocketTimeout(30000).build();
httpClient = HttpClients.custom().setDefaultRequestConfig(requestConfig).setConnectionManager(cm).build();
httpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
return httpRequestFactory;
}
}
Response Interceptor
public class ResponseInterceptor {
private HTTPInputProperties inputProperties;
private static final Logger LOGGER = LoggerFactory.getLogger(ResponseInterceptor.class);
/**
* Intercepts the errorMessage from the API response and sends appropriate
* information to the Output channel.
*
* #param errorMessage
* #return Message
*/
public Message<String> transform(Message<MessagingException> errorMessage) {
LOGGER.error("Inside Response Interceptor !");
Message<String> responseMessage = null;
try {
if (null != errorMessage && null != errorMessage.getPayload()
&& null != errorMessage.getPayload().getCause()) {
LOGGER.error("Cause is - " + errorMessage.getPayload().getCause().getMessage());
if (errorMessage.getPayload().getCause() instanceof HttpClientErrorException) {
HttpClientErrorException clientError = (HttpClientErrorException) errorMessage.getPayload()
.getCause();
LOGGER.error("Error in ResponseInceptor", clientError);
List<String> errorCodeList = getErrorCodes(inputProperties.getNonRetryErrorCodes());
// intercept Only those errors that are defined as
// nonRetryErrorCodes options in stream definition
if (null != clientError.getStatusCode()
&& errorCodeList.contains(clientError.getStatusCode().toString())) {
LOGGER.error("Error in Response Body", clientError.getResponseBodyAsString());
LOGGER.debug("Non retry message found. Sending to output channel without retrying");
responseMessage = MessageBuilder.withPayload((null == clientError.getResponseBodyAsString() || clientError.getResponseBodyAsString().isEmpty())
? getDefaultPayload(clientError.getStatusCode().toString()) : clientError.getResponseBodyAsString())
.setHeader(BatchHttpClientConstants.HTTP_STATUS, clientError.getStatusCode().toString())
.setHeader(BatchHttpClientConstants.REQUEST_OBJECT,
getFailedMessagePayload(errorMessage))
.copyHeaders(errorMessage.getPayload().getFailedMessage().getHeaders())
.setReplyChannelName(BatchHttpClientConstants.OUTPUT).setErrorChannelName(null).build();
} else {
LOGGER.debug("Status code from API is not present in the nonRetryCodes");
}
} else if (errorMessage.getPayload().getCause() instanceof HttpServerErrorException) {
LOGGER.error("Error is Instance of HttpServerErrorException");
HttpServerErrorException serverError = (HttpServerErrorException) errorMessage.getPayload()
.getCause();
responseMessage = MessageBuilder
.withPayload((null == serverError.getResponseBodyAsString()
|| serverError.getResponseBodyAsString().isEmpty())
? getDefaultPayload(serverError.getStatusCode().toString())
: serverError.getResponseBodyAsString())
.setHeader(BatchHttpClientConstants.HTTP_STATUS, serverError.getStatusCode().toString())
.setHeader(BatchHttpClientConstants.REQUEST_OBJECT, getFailedMessagePayload(errorMessage))
.copyHeaders(errorMessage.getPayload().getFailedMessage().getHeaders())
.setReplyChannelName(BatchHttpClientConstants.OUTPUT).setErrorChannelName(null).build();
}
}
} catch (Exception exception) {
LOGGER.error("Exception occured while transforming errorResponse", exception);
}
// returning null will send the message back to previous module
return responseMessage;
}
private String getDefaultPayload(String httpStatusCode) {
JSONObject jsonResponse = new JSONObject();
if (BatchHttpClientConstants.INTERNAL_SERVER_ERROR.equalsIgnoreCase(httpStatusCode)) {
jsonResponse.put(BatchHttpClientConstants.ID, BatchHttpClientConstants.INTERNAL_SERVER_ERROR_SUBCODE);
jsonResponse.put(BatchHttpClientConstants.TEXT, "Internal Server Error");
} else if (BatchHttpClientConstants.RESOURCE_NOT_FOUND.equalsIgnoreCase(httpStatusCode)) {
jsonResponse.put(BatchHttpClientConstants.ID, BatchHttpClientConstants.RESOURCE_NOT_FOUND_SUBCODE);
jsonResponse.put(BatchHttpClientConstants.TEXT, "Empty Response From the API");
}else{
jsonResponse.put(BatchHttpClientConstants.ID, BatchHttpClientConstants.GENERIC_ERROR_SUBCODE);
jsonResponse.put(BatchHttpClientConstants.TEXT, "Generic Error Occured.");
}
return jsonResponse.toString();
}
/**
* Get Individual error codes using delimiter
*
* #param nonRetryErrorCodes
* #return List of Error Codes as string
*/
private List<String> getErrorCodes(String nonRetryErrorCodes) {
List<String> errorCodeList = new ArrayList<String>();
StringTokenizer st = new StringTokenizer(nonRetryErrorCodes, BatchHttpClientConstants.DELIMITER);
while (st.hasMoreElements()) {
errorCodeList.add(st.nextToken());
}
return errorCodeList;
}
/**
* returns failed Message Payload
*
* #param errorMessage
* #return String
* #throws UnsupportedEncodingException
*/
private byte[] getFailedMessagePayload(Message<MessagingException> errorMessage)
throws UnsupportedEncodingException {
if (null != errorMessage.getPayload().getFailedMessage()
&& null != errorMessage.getPayload().getFailedMessage().getPayload()) {
return errorMessage.getPayload().getFailedMessage().getPayload().toString()
.getBytes(BatchHttpClientConstants.UTF_8);
}
return "".getBytes(BatchHttpClientConstants.UTF_8);
}
public HTTPInputProperties getInputProperties() {
return inputProperties;
}
public void setInputProperties(HTTPInputProperties inputProperties) {
this.inputProperties = inputProperties;
}
}
I can recommend an <aggregator> as a diagnostic tool.
Send the message to the <int-http:outbound-gateway> (or even better in the beginning of your flow - on input channel).
And send that message to the <aggregator>, too.
Some key from the message should be used as a correlationKey.
Expect reply from HTTP Gateway as a second message in group to release.
The ReleaseStrategy is standard MessageCountReleaseStrategy based on the size = 2.
And here is the main trick of the <aggregator> - group-timeout, which should be a bit more than socket timeout. The "uncompleted" group (only request) should be discarded to some other channel, where you will be able to report those undelivered messages and consult with your REST Service what's going on with them.

Spring integration gateway "Dispatcher has no subscribers"

I am getting an exception Dispatcher has no subscribers on the outboundChannel and can't figure out why. I am sure its something simple, I have stripped back my code to a very simple sample below:
My context is:
<bean id="requestService"
class="com.sandpit.RequestService" />
<integration:channel id="inboundChannel" />
<integration:service-activator id="service"
input-channel="inboundChannel"
output-channel="outboundChannel"
ref="requestService"
method="handleRequest" />
<integration:channel id="outboundChannel" />
<integration:gateway id="gateway"
service-interface="com.sandpit.Gateway"
default-request-channel="inboundChannel"
default-reply-channel="outboundChannel" />
<bean class="com.sandpit.GatewayTester">
<property name="gateway"
ref="gateway" />
</bean>
My Java code is:
public interface Gateway {
String receive();
void send(String message);
}
public class RequestService {
public String handleRequest(String request) {
return "Request received: " + request;
}
}
public class GatewayTester implements ApplicationListener<ContextRefreshedEvent> {
private Gateway gateway;
public void setGateway(Gateway gateway) {
this.gateway = gateway;
}
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
gateway.send("Hello world!");
System.out.println("FROM SERVICE: " + gateway.receive());
}
}
Note: A breakpoint does tell me that the RequestService is actually handling the request.
receive() with no args needs the reply channel to be a PollableChannel See the documentation.
add <queue/> to the outboundChannel.
Alternatively, You could change your gateway method to be String sendAndReceive(String in) and all will work as expected (and you can even remove the outboundChannel altogether).

WCF Restful Client throwing "The remote server returned an unexpected response: (405) Method Not Allowed."

I am a newbie at WCF. I created a WCF restful service in VS2010 (WCF service appl). It was targeted for Framework 4.0. I hosted this service on local IIS with appl pool set for framework 4.0. When I call the appl methods from browser or fiddler, they work fine. Now, I created a client console based. When I call any method from client I get the following Communication Exception:
** remote server returned an unexpected response: (405) Method Not Allowed.**
Service Interface file:
namespace MyService
{
[ServiceContract]
public interface ITestService
{
[WebGet(UriTemplate = "GetDateTime")]
[OperationContract]
string GetDateTime();
[WebGet(UriTemplate = "GetName")]
[OperationContract]
string GetName();
}
}
Class that implements the above interface:
namespace MyService
{
public class TestService : ITestService
{
public string GetDateTime()
{
return DateTime.Now.ToString();
}
public string GetName()
{
return "MY name is KingKong";
}
}
}
Web.config file:
<system.serviceModel>
<services>
<service behaviorConfiguration="ServiceBehavior" name="MyService.TestService">
<endpoint address="" behaviorConfiguration="web" binding="webHttpBinding" bindingConfiguration="" contract="MyService.ITestService">
</endpoint>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true" />
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
TestService.svc
<%# ServiceHost Language="C#" Debug="true" Service="MyService.TestService" CodeBehind="TestService.svc.cs" %>
Client app.config:
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="http://localhost/BestService/TestService.svc"
binding="webHttpBinding" bindingConfiguration="" contract="MyService.ITestService"
name="MyClientConfig" kind="" endpointConfiguration="" />
</client>
</system.serviceModel>
Client program calling proxy class
static void Main(string[] args)
{
TestServiceClient proxy = null;
try
{
proxy = new TestServiceClient();
Console.WriteLine("Test 1: List all products");
string sdatetime = proxy.GetName();
Console.WriteLine("Datetime: {0}", sdatetime);
Console.WriteLine();
// Disconnect from the service
proxy.Close();
}
catch (CommunicationException cex)
{
if (cex.InnerException != null)
{
Console.WriteLine("{0}", cex.InnerException.Message);
}
else
{
Console.WriteLine("General exception: {0}", cex.Message);
}
}
catch (Exception e)
{
if (e.InnerException != null)
{
Console.WriteLine("{0}", e.InnerException.Message);
}
else
{
Console.WriteLine("General exception: {0}", e.Message);
}
}
Console.WriteLine("Press ENTER to finish");
Console.ReadLine();
}
I added a service reference to the application and the Reference.cs file has following partial code:
namespace MyClient.MyService {
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="MyService.ITestService")]
public interface ITestService {
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/ITestService/GetDateTime", ReplyAction="http://tempuri.org/ITestService/GetDateTimeResponse")]
string GetDateTime();
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/ITestService/GetName", ReplyAction="http://tempuri.org/ITestService/GetNameResponse")]
string GetName();
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public interface ITestServiceChannel : MyClient.MyService.ITestService, System.ServiceModel.IClientChannel {
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public partial class TestServiceClient : System.ServiceModel.ClientBase<MyClient.MyService.ITestService>, MyClient.MyService.ITestService {
public TestServiceClient() {
}
public TestServiceClient(string endpointConfigurationName) :
base(endpointConfigurationName) {
}
public TestServiceClient(string endpointConfigurationName, string remoteAddress) :
base(endpointConfigurationName, remoteAddress) {
}
public TestServiceClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
base(endpointConfigurationName, remoteAddress) {
}
public TestServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(binding, remoteAddress) {
}
public string GetDateTime() {
return base.Channel.GetDateTime();
}
public string GetName() {
return base.Channel.GetName();
}
}
}
Please help me as I have spent nearly 2 days trying to figure out the problem in the client
Thanks
VS2010 doesnt have the ability to generate a proxy for a RESTFUL service as it does for a SOAP based service. So the solution is write your own proxy class which inherits from the ClientBase class and use the Channel to call the web methods of the service.

Resources