ClosedChannelException на FluentProducerTemplate с Apache Camel - PullRequest
0 голосов
/ 30 октября 2018

Я сталкиваюсь с ClosedChannelException с моим приложением, когда тестирую маршрут Apache Camel.

rest().post("/{{camel.rest.version}}/ufx")
    .type(MyMsg.class)
    .consumes(MediaType.APPLICATION_XML_VALUE)
    .produces(MediaType.APPLICATION_XML_VALUE)
    .responseMessage().code(HttpStatus.SC_OK).responseModel(MyMsg.class).endResponseMessage()
    .responseMessage().code(HttpStatus.SC_BAD_REQUEST).responseModel(MyMsg.class).endResponseMessage()
    .route().routeId("rst_myrest")      
    .to("direct:route_myroute")
    .endRest();

onException(ValidationException.class).
    handled(true).
    log(LoggingLevel.ERROR, LoggingConst.ERROR +" Validation exception: ${body}").
    onExceptionOccurred(validationExceptionProcessor).
    marshal().jaxb(MyMsg.class.getPackage().getName()).
    log(LoggingLevel.INFO, "Error response ${body}").
    end();  

from("direct:route_myroute").routeId("rt_ufx")      
    .unmarshal().jaxb(MyMsg.class.getPackage().getName())
    .process("myvalidator")
    //some other lines
    .marshal().jaxb(MyMsg.class.getPackage().getName())
    .end(); 

В моем тесте передаются неверные данные, поэтому вызывается ValidationExceptionProcessor.

public class ValidationExceptionProcessor implements Processor {
    @Override
    public void process(Exchange exchange) throws Exception {
        ValidationException request = exchange.getException(ValidationException.class);
        MyMsg msg = new MyMsg();
        //preparing some pretty object
        exchange.getOut().setBody(msg);
        exchange.getOut().setHeader("CamelHttpResponseCode", HttpStatus.BAD_REQUEST);
    }
}

Работает нормально, когда я вызываю свое приложение напрямую (например, из Почтальона). Правильный MyMsg объект зарегистрирован и возвращен пользователю. Junit-test FluentProducerTemplate используется для проверки результата:

@RunWith(CamelSpringBootRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@DirtiesContext
@Slf4j
@UseAdviceWith
public class myTest {

    @Produce(uri = "undertow:myaddress")
    FluentProducerTemplate holdProducer;

    @Test
    public void invalidDataException() throws IOException, InterruptedException {
        Exchange send = holdProducer.withHeader(Exchange.HTTP_METHOD, HttpMethod.POST)
                .withHeader(Exchange.CONTENT_TYPE, MediaType.APPLICATION_XML_VALUE)
                .withBody(Files.readAllBytes(Paths.get("src/test/resources/Request.xml")))
                .send();
        Exception exception = send.getException(); //here I get java.nio.channels.ClosedChannelException
    }
}

Журналы:

{"timestamp":"2018-10-30T15:04:12.779+00:00","level":"WARN","logger_name":"org.apache.camel.util.IOHelper","stack_trace":"java.nio.channels.ClosedChannelException: null
    at io.undertow.server.protocol.http.HttpResponseConduit.processWrite(HttpResponseConduit.java:122)
    at io.undertow.server.protocol.http.HttpResponseConduit.write(HttpResponseConduit.java:596)
    at io.undertow.conduits.ChunkedStreamSinkConduit.flush(ChunkedStreamSinkConduit.java:267)
    at org.xnio.conduits.ConduitStreamSinkChannel.flush(ConduitStreamSinkChannel.java:162)
    at io.undertow.channels.DetachableStreamSinkChannel.flush(DetachableStreamSinkChannel.java:119)
    at org.xnio.channels.Channels.flushBlocking(Channels.java:63)
    at io.undertow.servlet.spec.ServletOutputStreamImpl.close(ServletOutputStreamImpl.java:612)
    at org.apache.camel.util.IOHelper.close(IOHelper.java:342)
    at org.apache.camel.util.IOHelper.close(IOHelper.java:406)
    at org.apache.camel.util.IOHelper.close(IOHelper.java:416)
    at org.apache.camel.http.common.DefaultHttpBinding.copyStream(DefaultHttpBinding.java:434)
    at org.apache.camel.http.common.DefaultHttpBinding.doWriteDirectResponse(DefaultHttpBinding.java:496)
    at org.apache.camel.http.common.DefaultHttpBinding.doWriteResponse(DefaultHttpBinding.java:395)
    at org.apache.camel.http.common.DefaultHttpBinding.writeResponse(DefaultHttpBinding.java:322)
    at org.apache.camel.http.common.CamelServlet.doService(CamelServlet.java:209)
    at org.apache.camel.http.common.CamelServlet.service(CamelServlet.java:73)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
    at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:208)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:105)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilte    rInternal(HiddenHttpMethodFilter.java:81)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61)
    at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131)
    at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84)
    at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
    at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
    at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:131)
    at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
    at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64)
    at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
    at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
    at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:292)
    at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
    at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
    at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
    at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
    at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
    at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
    at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
    at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
    at io.undertow.server.Connectors.executeRootHandler(Connectors.java:211)
    at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:809)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
","message":"Cannot close. Reason: null","camel.exchangeId":"ID-VRN26-1540911805592-0-3","camel.contextId":"","camel.breadcrumbId":"ID-VRN26-1540911805592-0-1","camel.messageId":"ID-VRN26-1540911805592-0-4"}

В чем разница между вызовом из Fluent и работающим приложением напрямую? Как это можно исправить?

P.S. Чангирование вывода сообщения в In () не помогает

1 Ответ

0 голосов
/ 31 октября 2018

Я нашел решение, но не там, где искал. Вот мой кодовый набор Http:

exchange.getOut().setHeader("CamelHttpResponseCode", HttpStatus.BAD_REQUEST);

HttpStatus.BAD_REQUEST является записью перечисления Spring. И результатом является значение enum:

BAD_REQUEST(400, "Bad Request"),

Когда мое приложение запускалось в контейнере Spring, Spring сделал unmarshal значения enum, и я получил правильное тело с кодом Http-400.

Однако с FluentProducerTemplate (с принадлежностью к верблюжьим библиотекам) не было выполнено демаршаллинга, и беглый сбой где-то. (Мне интересно, где и почему именно. И почему ошибка такая странная).

Переход на заголовок Camel решил мою проблему

 exchange.getOut().setHeader("CamelHttpResponseCode", HttpStatus.SC_BAD_REQUEST); 

потому что httpHeaders Camel - не перечисление, а общедоступное поле static-final

public static final int SC_BAD_REQUEST = 400;

также работает нормально, если вы вызываете .value () для Spring enum

exchange.getOut().setHeader("CamelHttpResponseCode", HttpStatus.BAD_REQUEST.value());
...