简体   繁体   中英

Spring Integration application with external Web Services monitoring

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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM