I am trying to configure the a HttpRequestExecutingMessageHandler
#Bean
public HttpRequestExecutingMessageHandler httpEnrollHandlerKairos() {
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler(kairosProperties.getEnrollUrl());
handler.setOutputChannel(channelConfiguration.kairosResponseChannel());
handler.setExpectReply(true);
handler.setMessageConverters(httpMessageConverters);
handler.setHttpMethod(HttpMethod.POST);
handler.setExpectedResponseType(EnrollResponse.class);
handler.setHeaderMapper(defaultHeaderMapper);
return handler;
}
And with the following flow.
public IntegrationFlow kairosEnrollFlow() {
return IntegrationFlows
.from(channelConfiguration.kairosEnrollChannel())
.<Agreement , EnrollRequest>transform(p -> transformAgreementEnrollRequest(p))
.transform(Transformers.toJson())
.headerFilter(httpRequestHeaderFilters())
.enrichHeaders(m -> m.header(APP_ID, kairosProperties.getAppId()))
.enrichHeaders(m -> m.header(APP_KEY, kairosProperties.getAppKey()))
.handle(httpEnrollHandlerKairos())
.get();
}
I put an enrichHeaders on this flow for the errorChannel with no results...
I configure an Autowired PS CHannel for the errorChannel as follows,
#Autowired
#Qualifier("errorChannel")
private PublishSubscribeChannel errorChannel;
My error handler flow... (redirect to another errorChannel for convenience purposes) my objective is to configure specific error channels for some sort of different problems on the application.
#Autowired
#Qualifier("errorChannel")
private PublishSubscribeChannel errorChannel;
#Autowired
private ChannelConfiguration channelConfiguration;
#Bean
public IntegrationFlow errorHandlerFlow() {
return IntegrationFlows.from(errorChannel)
.<Message<?> , Message<ErrorMessage>>transform(m -> handleExceptionMessage(m))
.channel(channelConfiguration.errorHandlerOutputChannel())
.get();
}
public Message<ErrorMessage> handleExceptionMessage(Message<?> message)
{
MessagingException me = (MessagingException) message.getPayload();
MessageHeaders mh = me.getFailedMessage().getHeaders();
return MessageBuilder.withPayload(new ErrorMessage()).copyHeaders(mh).build();
}
But afterhundred of tries i couldn't put this errorChannel to work, the application on the send method fires an exception, and the errorChannel and the "errorFlow" configured is never called.
I Noticed that the application Handler is using teh GenericMessagingTemplate that on it turns has the following code.
I tried to use the headers (Message and IntegrationUtils), and nothin " TemporaryReplyChannel tempReplyChannel = new TemporaryReplyChannel(this.throwExceptionOnLateReply);
requestMessage = MessageBuilder.fromMessage(requestMessage).setReplyChannel(tempReplyChannel)
.setHeader(this.sendTimeoutHeader, null)
.setHeader(this.receiveTimeoutHeader, null)
.setErrorChannel(tempReplyChannel).build();".
which does not let to override the errorChannel never...
Any tip how to do this.
Does anyone knows what happens, all channels on question are asynchronous.
Any help to clarify the configuration of the error channel will be glad.
Kind Regards,
José Carlos Canova.
Related
I want to create two beans of PubSubTemplate class to set different message converter.
I am having two subscriber among them one is receiving Json response and another one is receiving String response. To handle these two scenario I am creating two PubSubTemplate bean.
Below is my PubSubTemplateConfig.java :
#Configuration
public class PubSubTemplateConfig {
#Bean
public PubSubTemplate pubSubTemplateForUserCreation(PubSubPublisherTemplate pubSubPublisherTemplate,
PubSubSubscriberTemplate pubSubSubscriberTemplate) {
PubSubTemplate template = new PubSubTemplate(pubSubPublisherTemplate, pubSubSubscriberTemplate);
template.setMessageConverter(new JacksonPubSubMessageConverter(getObjectMapper()));
return template;
}
private ObjectMapper getObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
return objectMapper;
}
#Bean
public PubSubTemplate pubSubTemplateForAuditTracker(PubSubPublisherTemplate pubSubPublisherTemplate,
PubSubSubscriberTemplate pubSubSubscriberTemplate) {
PubSubTemplate template = new PubSubTemplate(pubSubPublisherTemplate, pubSubSubscriberTemplate);
template.setMessageConverter(new SimplePubSubMessageConverter());
return template;
}
}
Below two are the subscriber configuration :
AuditsubscriptioncriptionConfiguration.java
#Configuration
public class AuditsubscriptioncriptionConfiguration {
#Value("${subscriptioncription.auditsubscriptioncription}")
private String subscription;
#Bean("pubsubAuditInputChannel")
public MessageChannel pubsubAuditInputChannel() {
return new DirectChannel();
}
#Bean
public PubSubInboundChannelAdapter auditMessageChannelAdapter(#Qualifier("pubsubAuditInputChannel") MessageChannel pubsubAuditInputChannel,
#Qualifier("pubSubTemplateForAuditTracker") PubSubTemplate pubSubTemplateForAuditTracker) {
PubSubInboundChannelAdapter adapter = new PubSubInboundChannelAdapter(pubSubTemplateForAuditTracker, subscription);
adapter.setOutputChannel(pubsubAuditInputChannel);
adapter.setPayloadType(String.class); //need changes
adapter.setAckMode(AckMode.MANUAL);
return adapter;
}
}
And UserSubscriptionConfiguration.java
#Configuration
public class UserSubscriptionConfiguration {
#Value("${subscription.userSubscriber}")
private String subscriber;
#Bean("pubsubInputChannel")
public MessageChannel pubsubInputChannel() {
return new DirectChannel();
}
#Bean
public PubSubInboundChannelAdapter userMessageChannelAdapter(#Qualifier("pubsubInputChannel") MessageChannel pubsubInputChannel,
#Qualifier("pubSubTemplateForUserCreation") PubSubTemplate pubSubTemplateForUserCreation) {
PubSubInboundChannelAdapter adapter = new PubSubInboundChannelAdapter(pubSubTemplateForUserCreation, subscriber);
adapter.setOutputChannel(pubsubInputChannel);
adapter.setPayloadType(UserChangeEvent.class);
adapter.setAckMode(AckMode.MANUAL);
return adapter;
}
}
Steps I observed during container start up :
Step 1. First pubSubTemplateForAuditTracker bean is getting created with SimplePubSubMessageConverter and then AuditMessageChannelAdapter bean is getting configured
Step 2. pubSubTemplateForUserCreation bean is getting created with JacksonPubSubMessageConverter and userMessageChannelAdapter is getting configured
Here, I should have two beans with two different message converter but while debugging I found that only one instance of PubSubTemplate is present and the attached message converter is JacksonPubSubMessageConverter. pubSubTemplateForAuditTracker bean is getting overridden with pubSubTemplateForUserCreation bean though I had defined them twice with #Bean annotation. This behavior is leading to an Error when auditMessageChannelAdapter is receiving a String message
My expectation is I want to have two separate PubSubTemplate bean with two different Message Converter.
Basically I want to create two beans of type PubSubTemplate with different behaviour.
Can someone please help me here.
I am exploring GCP pub/sub for the first time. Thank you
You can manually create multiple subscriptions like this
#PostConstruct
private void subscribeWithConcurrencyControl() {
// create subscription
TopicName topic = TopicName.ofProjectTopicName(projectId, this.eventTopic);
Subscription subscription = Subscription.newBuilder()
.setName("projects/XYZ/subscriptions/" + eventSubscription)
.setTopic(topic.toString())
.setPushConfig(PushConfig.getDefaultInstance())
.setAckDeadlineSeconds(100)
.build();
Subscription subscription2 = Subscription.newBuilder()
.setName("projects/XYZ/subscriptions/" + eventSubscription2)
.setTopic(topic.toString())
.setPushConfig(PushConfig.getDefaultInstance())
.setAckDeadlineSeconds(100)
.build();
try {
client.createSubscription(subscription);
} catch (AlreadyExistsException e) {
// nothing to do
}
try {
client.createSubscription(subscription2);
} catch (AlreadyExistsException e) {
// nothing to do
}
ProjectSubscriptionName subscriptionName1 = ProjectSubscriptionName.of(projectId, eventSubscription);
ProjectSubscriptionName subscriptionName2 = ProjectSubscriptionName.of(projectId, eventSubscription2);
// Instantiate an asynchronous message receiver.
MessageReceiver receiver = (PubsubMessage message, AckReplyConsumer consumer) -> {
// Handle incoming message, then ack the received message.
try {
process(message);
consumer.ack();
} catch (Exception e) {
LOG.error("Failed to process message", e);
consumer.nack();
}
};
// Provides an executor service for processing messages. The default `executorProvider` used
// by the subscriber has a default thread count of 5.
ExecutorProvider executorProvider =
InstantiatingExecutorProvider.newBuilder().setExecutorThreadCount(2).build();
FlowControlSettings flowControlSettings =
FlowControlSettings.newBuilder()
.setMaxOutstandingElementCount(100L)
.build();
// `setParallelPullCount` determines how many StreamingPull streams the subscriber will open
// to receive message. It defaults to 1. `setExecutorProvider` configures an executor for the
// subscriber to process messages. Here, the subscriber is configured to open 2 streams for
// receiving messages, each stream creates a new executor with 4 threads to help process the
// message callbacks. In total 2x4=8 threads are used for message processing.
subscriber1 = Subscriber.newBuilder(subscriptionName1, receiver)
.setParallelPullCount(20)
.setExecutorProvider(executorProvider)
.setCredentialsProvider(credentialsProvider)
.setFlowControlSettings(flowControlSettings)
.build();
subscriber2 = Subscriber.newBuilder(subscriptionName2, receiver)
.setParallelPullCount(20)
.setExecutorProvider(executorProvider)
.setCredentialsProvider(credentialsProvider)
.setFlowControlSettings(flowControlSettings)
.build();
// Start the subscriber.
subscriber1.startAsync().awaitRunning();
subscriber2.startAsync().awaitRunning();
}
In the process() method you can use the usual objectmapper and/or instanceof commands to determine the type of the message (or have different receivers for different subscriptions, or even transport the type of the message in the pubsub headers)
private void process(PubsubMessage message) {
try {
ModificationEvent modificationEvent = objectMapper.readValue(message.getData().toStringUtf8(), ModificationEvent.class);
} catch(...)
I'm using Spring integration and scatter gather pattern in my project. Here I've applied the Rest Template timeout and also I'm using ExpressionEvaluatingRequestHandlerAdvice() to catch timeout exception. But I want to catch that exception in failure flow and want to throw my own custom exception and I have my own exception handler in place to handle that exception so that I can show proper error message to the user. But here the exception is being thrown but my custom exception handler is not able to handle that exception so user is not getting my custom msg back.
//configuration
#Bean
public IntegrationFlow mainFlow() {
return flow ->
flow.split()
.channel(c -> c.executor(Executors.newCachedThreadPool()))
.scatterGather(
scatterer ->
scatterer
.applySequence(true)
.recipientFlow(flow1())
.recipientFlow(flow2()),
gatherer ->
gatherer
.releaseLockBeforeSend(true)
.releaseStrategy(group -> group.size() == 1))
.aggregate()
.to(anotherFlow());
}
#Bean
public IntegrationFlow flow2() {
return flow -> {
flow.channel(c -> c.executor(Executors.newCachedThreadPool()))
.handle(
Http.outboundGateway(
"http://localhost:4444/test", dummyService.restTemplate())
.httpMethod(HttpMethod.POST)
.expectedResponseType(String.class),
c -> c.advice(expressionAdvice()));
};
}
#Bean
public Advice expressionAdvice() {
ExpressionEvaluatingRequestHandlerAdvice advice =
new ExpressionEvaluatingRequestHandlerAdvice();
advice.setSuccessChannelName("success.input");
advice.setOnSuccessExpressionString("payload + ' was successful'");
advice.setFailureChannelName("failure.input");
advice.setOnFailureExpressionString("'Failed ' + #exception.cause.message");
advice.setReturnFailureExpressionResult(true);
advice.setTrapException(true);
return advice;
}
#Bean
public IntegrationFlow success() {
return f -> f.handle(System.out::println);
}
#Bean
public IntegrationFlow failure() {
return f ->
f.handle(
(p, h) -> {
if (p.toString().contains("Read timed out"))
throw new MyCustomException(ErrorCode.TIMEOUT_ERROR.getErrorData());
else throw new MyCustomException(ErrorCode.SERVICE_ERROR.getErrorData());
});
}
//DummyService class
#Configuration
public class DummyService {
private final int TIMEOUT = (int) TimeUnit.SECONDS.toMillis(6);
#Bean
public RestTemplate restTemplate()
{
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
requestFactory.setConnectTimeout(TIMEOUT);
requestFactory.setReadTimeout(TIMEOUT);
RestTemplate restTemplate = new RestTemplate(requestFactory);
return restTemplate;
}
}
Here I'm trying to throw new exception in failure() flow but exception is being thrown properly but my custom exception handler framework is not able to catch that exception. In all other cases it's able to catch but inside the spring integration configuration class it's not working.
When you do an async hand-off like with your flow.channel(c -> c.executor(Executors.newCachedThreadPool())), you cannot re-throw exceptions from that thread: the control is lost. You can have an errorChannel header populated for this flow, so an async ErrorHandler on the task executor is going to publish the ErrorMessage properly to that channel for some logic. From there you can return so-called compensation message for waiting gateway - gatherer in your case. What is very important is to preserve request headers. Otherwise it won't be able to correlated request with reply.
UPDATE
Your understanding of async error handling is a bit not correct.
The ExecutorChannel wraps a provided Executor into an ErrorHandlingTaskExecutor, which has the logic like this:
public void execute(final Runnable task) {
this.executor.execute(() -> {
try {
task.run();
}
catch (Throwable t) { //NOSONAR
ErrorHandlingTaskExecutor.this.errorHandler.handleError(t);
}
});
}
So, as you see that errorHandler is called inside the task, where re-throwing an exception from there will just end-user in void, according to your ThreadPoolExecutor:
protected void afterExecute(Runnable r, Throwable t) { }
That's why we talk about a compensation message instead of re-throwing some custom exception. That compensation message, though, could be a new ErrorMessage, but it has to preserve headers from request. something like this:
#ServiceActivator(inputChannel = "scatterGatherErrorChannel")
public Message<?> processAsyncScatterError(MessagingException payload) {
return MessageBuilder.withPayload(payload.getCause())
.copyHeaders(payload.getFailedMessage().getHeaders())
.build();
}
This message is going to be returned to the scatter-gather as a reply. Probably you are not interested in this, so you can bypass a reply for scatter-gather and propagate your custom error directly to the origin gateway's error channel. Something like this:
#ServiceActivator(inputChannel = "scatterGatherErrorChannel")
public Message<?> processAsyncScatterError(MessagingException payload) {
MessageHeaders requestHeaders = payload.getFailedMessage().getHeaders();
return MessageBuilder.withPayload(payload.getCause())
.copyHeaders(requestHeaders)
.setHeader(MessageHeaders.REPLY_CHANNEL, requestHeaders.get("originalErrorChannel"))
.build();
}
That originalErrorChannel comes really from the request message to the scatter-gather and is waited by the origin gateway.
I have implemented the example as shown here Spring Dynamic Destination
In the rabbitmq, it is creating an exchange dynamically, but there is no option to provide binding or routing key. My requirement is to send a message to this dynamically created exchange with a routing key. How would i need to implement this to setup the routing key?
#Component
public class DDProducerBean {
#Autowired
private BinderAwareChannelResolver poChannelResolver = null;
public void publish(DDSocketVO ddSocketVO) throws Exception {
this.poChannelResolver.resolveDestination(ddSocketVO.getDestination()).send(MessageBuilder.withPayload(new ObjectMapper().
setVisibility(PropertyAccessor.FIELD, Visibility.ANY).
writeValueAsString(ddSocketVO)).build());
}
}
Here is the workaround as suggested Here
Basically create a MessageChannel with the dynamic destination using BinderAwareChannelResolver, then connect to RabbitMQ with RabbitAdmin API and bind the newly created exchange to another queue or exchange with routing key before sending messages.
#Autowired
private BinderAwareChannelResolver poChannelResolver;
public void publish(WebSocketVO webSocketVO) throws Exception {
MessageChannel channel = this.poChannelResolver.resolveDestination(webSocketVO.getDestination());
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setUsername(System.getProperty("spring.cloud.stream.binders.corerabbit.environment.spring.rabbitmq.username"));
connectionFactory.setPassword(System.getProperty("spring.cloud.stream.binders.corerabbit.environment.spring.rabbitmq.password"));
connectionFactory.setAddresses(System.getProperty("spring.cloud.stream.binders.corerabbit.environment.spring.rabbitmq.addresses"));
connectionFactory.setVirtualHost(System.getProperty("spring.cloud.stream.binders.corerabbit.environment.spring.rabbitmq.virtual-host"));
AmqpAdmin amqpAdmin = new RabbitAdmin(connectionFactory);
TopicExchange sourceExchange = new TopicExchange(webSocketVO.getDestination(), false, true);
TopicExchange destExchange = new TopicExchange("amq.topic");
amqpAdmin.declareBinding(BindingBuilder.bind(destExchange).to(sourceExchange).with(webSocketVO.getRoutingKeyExpression()));
channel.send(MessageBuilder.withPayload(new ObjectMapper().
setVisibility(PropertyAccessor.FIELD, Visibility.ANY).
writeValueAsString(webSocketVO)).build());
amqpAdmin.deleteExchange(webSocketVO.getDestination());
connectionFactory.destroy();
}
I am the newbie on Spring Integration and EIP.
I tried to solve myself but it would not come proper solution on my mind.
Here is the example of the flow I need.
Client send Json data to SI.
HTTP inbound Gateway got Json data as payload.
This Json data will be used two separate HTTP outbound.
But the second outbound call is followed on the response of the first call outbound call.
reply the result to Step 0 (replychannel on HTTP inbound Gateway)
I am thinking to use publishSubscribeChannel or RecipientListRouter to send the same message to separate calls.
but in this case, it need to execute the second http outbound on certain time.
And as I understand it should be one transaction for the reply on step 4.
Or...
Is it possible keep the message instance (JSON data from http inbound Gateway) to use at the second call after the first call.
Please give me some idea and if you have a sample code, it must be wonderful.
Currently I am write JAVA config style but XML sample also welcome.
Update the question
I am trying to add the bridge and executorChannel.
Here is my code and few thing are not working as I expected.
I want to the second outbound handler(httpPMGateway) use the message from aIncomingGateway(using publishSubscribeChannel because of this) , but after httpJ3Gateway outbound handler, it use the message from the httpJ3Gateway.
I use the route with response message from httpJ3Gateway.
(Ok -> httpPMgateway , Default -> backAChannel)
I would like to send response message to aIncomingGateway from end of j3ValidationFlow or broadcastFlow. But aIncomingGateway got reply message already before they send. this is log message.
2017-05-16 11:17:09.061 WARN 11488 --- [pool-1-thread-1]
cMessagingTemplate$TemporaryReplyChannel : Reply message received but
the receiving thread has already received a reply:
This is code section :
#Bean
public MessagingGatewaySupport aIncomingGateway() {
HttpRequestHandlingMessagingGateway handler = new HttpRequestHandlingMessagingGateway(true);
RequestMapping requestMapping = new RequestMapping();
requestMapping.setMethods(HttpMethod.POST);
requestMapping.setPathPatterns("/a/incoming");
requestMapping.setConsumes("application/json");
requestMapping.setProduces("application/json");
handler.setRequestMapping(requestMapping);
handler.setMessageConverters(getMessageConverters());
handler.setRequestPayloadType(Amount.class);
handler.setReplyChannelName("backAChannel");
return handler;
}
#Bean
public MessageHandler httpJ3Gateway(){
HttpRequestExecutingMessageHandler httpHandler = new HttpRequestExecutingMessageHandler("http://localhost:8080/j3/incoming");
httpHandler.setHttpMethod(HttpMethod.POST);
httpHandler.setExpectReply(true);
httpHandler.setMessageConverters(getMessageConverters());
httpHandler.setExpectedResponseType(String.class);
httpHandler.setRequestFactory(requestFactory());
return httpHandler;
}
#Bean
public MessageHandler httpPMGateway(){
HttpRequestExecutingMessageHandler httpHandler = new HttpRequestExecutingMessageHandler("http://localhost:8080/pm/incoming");
httpHandler.setHttpMethod(HttpMethod.POST);
httpHandler.setExpectReply(true);
httpHandler.setMessageConverters(getMessageConverters());
httpHandler.setExpectedResponseType(String.class);
return httpHandler;
}
#Bean
public MessageChannel broadChannel(){
return new ExecutorChannel(Executors.newCachedThreadPool());
}
#Bean
#ServiceActivator(inputChannel = "brigdeChannel")
public MessageHandler bridgeHandler(){
BridgeHandler handler = new BridgeHandler();
handler.setOutputChannel(broadChannel());
return handler;
}
#Bean
public IntegrationFlow aIncomingFlow() {
return IntegrationFlows.from(aIncomingGateway())
.log(LoggingHandler.Level.INFO, "From Inbound http gateway", m -> m.getPayload().toString())
.publishSubscribeChannel(p -> p
.subscribe(s -> s.channel("j3Channel"))
.subscribe(s -> s.bridge(b -> bridgeHandler()) )
)
.get();
}
#Bean
public IntegrationFlow j3ValidationFlow() {
return IntegrationFlows.from("j3Channel")
.log(LoggingHandler.Level.INFO, "Run j3ChannelFlow", m -> m.getPayload().toString())
.handle(httpJ3Gateway())
.route("headers.http_statusCode", m -> m
.resolutionRequired(false)
.channelMapping("OK", "brigdeChannel")
.defaultOutputToParentFlow()
)
.channel("backAChannel")
.get();
}
#Bean
public IntegrationFlow broadcastFlow() {
return IntegrationFlows.from("broadChannel")
.log(LoggingHandler.Level.INFO, "Run broadcastFlow", m -> m.getPayload().toString())
.handle(httpPMGateway())
.channel("backAChannel")
.get();
}
Please help me to get an idea.
I appreciated in advance. Thank you for helping me on this dumb question.
Yes; use a pub/sub channel or a recipient list router
inbound -> ... -> pubsub
pubsub -> outbound1
pubsub -> bridge -> executorChannel -> outbound2
outbound1 will run on the inbound thread; outbound2 will run on another thread.
Currently I have an application with Spring Integration DSL that has AMQP inbound gateway with different service activators, each service activator has kind of logic to decide, transform and call external web services (currently with CXF), but all this logic is in code without Spring Integration components.
These service activators are monitored, in the output channel that returns data from this application is an AMQP adapter that sends headers to a queue (after that, all headers are processed and saved in a database for future analysis). This works well, these service activators even have elapsed time in headers.
Now, the problem is, that I need to monitor external web service calls, like elapsed time in each operation, which service endpoint and operation was called, if an error occurred.
I've been thinking that logic code in each service activator should be converted into a Spring Integration flow, in each service activator, would call a new gateway with the name of the operation of the web service in a header, and monitoring every flow as currently I had been doing.
So, I'm not sure if this manual approach is the better approach, so I wonder if there is a way to get the name of the service operation with some kind of interceptor or something similar with CXF or Spring WS to avoid setting the name of the operation in headers in a manual way? What would be your recommendation?
To have more context here is the Spring Integration configuration:
#Bean
public IntegrationFlow inboundFlow() {
return IntegrationFlows.from(Amqp.inboundGateway(simpleMessageListenerContainer())
.mappedReplyHeaders(AMQPConstants.AMQP_CUSTOM_HEADER_FIELD_NAME_MATCH_PATTERN)
.mappedRequestHeaders(AMQPConstants.AMQP_CUSTOM_HEADER_FIELD_NAME_MATCH_PATTERN)
.errorChannel(gatewayErrorChannel())
.requestChannel(gatewayRequestChannel())
.replyChannel(gatewayResponseChannel())
)
.enrichHeaders(new Consumer<HeaderEnricherSpec>() {
#Override
public void accept(HeaderEnricherSpec t) {
t.headerExpression(AMQPConstants.START_TIMESTAMP, "T(java.lang.System).currentTimeMillis()");
}
})
.transform(getCustomFromJsonTransformer())
.route(new HeaderValueRouter(AMQPConstants.OPERATION_ROUTING_KEY))
.get();
}
#Bean
public MessageChannel gatewayRequestChannel() {
return MessageChannels.publishSubscribe().get();
}
#Bean
public MessageChannel gatewayResponseChannel() {
return MessageChannels.publishSubscribe().get();
}
private IntegrationFlow loggerOutboundFlowTemplate(MessageChannel fromMessageChannel) {
return IntegrationFlows.from(fromMessageChannel)
.handle(Amqp.outboundAdapter(new RabbitTemplate(getConnectionFactory()))
.exchangeName(LOGGER_EXCHANGE_NAME)
.routingKey(LOGGER_EXCHANGE_ROUTING_KEY)
.mappedRequestHeaders("*"))
.get();
}
And here is a typical service activator, as you can see, all this logic could be an integration flow:
#ServiceActivator(inputChannel="myServiceActivator", outputChannel = ConfigurationBase.MAP_RESPONSE_CHANNEL_NAME)
public Message<Map<String, Object>> myServiceActivator(Map<String, Object> input, #Header(AMQPConstants.SESSION) UserSession session) throws MyException {
Message<Map<String, Object>> result = null;
Map<String, Object> mapReturn = null;
ExternalService port = serviceConnection.getExternalService();
try {
if (input.containsKey(MappingConstants.TYPE)) {
Request request = transformer
.transformRequest(input, session);
Response response = port
.getSomething(request);
utils.processBackendCommonErrors(response.getCode(), response.getResponse());
mapReturn = transformer.convertToMap(response);
} else {
Request request = transformer
.transformRequest(input, session);
Response response = port
.getSomethingElse(request);
utils.processBackendCommonErrors(response.getCode(),
response.getResponse());
mapReturn = transformer.convertToMap(response);
}
} catch (RuntimeException e) {
String message = "unexcepted exception from the back-end";
logger.warn(message, e);
throw MyException.generateTechnicalException(message, null, e);
}
result = MessageBuilder.withPayload(mapReturn)
.build();
return result;
}
So far so good. Or I don't understand the problem, or you are not clear where it is.
Anyway you always can proxy any Spring Service with the AOP, since it looks like you are pointing to the code:
Response response = port
.getSomething(request);
When this (or similar) method is called, some MethodInterceptor can perform desired tracing logic and send result to some MessageChannel for further analysis or anything else to do:
public Object invoke(MethodInvocation invocation) throws Throwable {
// Extract required operation name and start_date from the MethodInvocation
Object result = invocation.proceed();
// Extract required data from the response
// Build message and send to the channel
return result;
}