0
votes

I have a weird issue that happened after upgrading from Camel 2.17 to 2.18. In my router class, I have the following snippet to process incoming requests. Good requests get passed down the chain, while specific errors get targeted for error handling like so:

application.yml

camel.endpoint.my.servletEndpoint: servlet:///activate?httpMethodRestrict=POST,OPTIONS

CamelRouter.java

from("{{camel.endpoint.my.servletEndpoint}}")
            .doTry()
                .process(myRequestProcessor)
                .to("{{camel.endpoint.my.trigger}}")
            .doCatch(IllegalArgumentException.class)
                .to("{{camel.endpoint.httpError.badRequest}}")
            .doCatch(IllegalStateException.class)
                .to("{{camel.endpoint.httpError.badState}}")
            .end();

// HTTP Error endpoints
from("{{camel.endpoint.httpError.badRequest}}")
        .process(new HttpErrorHandler(HttpStatus.BAD_REQUEST));
from("{{camel.endpoint.httpError.badState}}")
        .process(new HttpErrorHandler(HttpStatus.INTERNAL_SERVER_ERROR));

MyRequestProcessor.java

public class RedcapRequestProcessor implements Processor {
...
@Override
public void process(Exchange exchange) throws Exception {
    MyTriggerRequest triggerRequest = new MyTriggerRequest();

    HttpServletRequest request = exchange.getIn().getHeader(Exchange.HTTP_SERVLET_REQUEST, HttpServletRequest.class);
    // If request doesn't look right, throw IllegalArgumentException

    if (!getAndVerifyParameter(request, "url_under_test_parameter"))
    throw new IllegalArgumentException("Not a good parameter!");

    // Add to outbound message
    exchange.getOut().setBody(triggerRequest);
}

}

This worked as expected in Camel 2.17 - when MyRequestProcessor would throw the IllegalArgumentException during processing, the router class would catch it and handle accordingly. However after upgrading, I am now getting an IOException: Stream closed error, apparently because the HTTP response Exchange has been closed for some reason? The stack trace:

org.apache.camel.RuntimeCamelException: java.io.IOException: Stream closed
at org.apache.camel.http.common.HttpMessage.createBody(HttpMessage.java:74)
at org.apache.camel.impl.MessageSupport.getBody(MessageSupport.java:47)
at org.apache.camel.http.common.DefaultHttpBinding.doWriteResponse(DefaultHttpBinding.java:386)
at org.apache.camel.http.common.DefaultHttpBinding.writeResponse(DefaultHttpBinding.java:322)
at org.apache.camel.http.common.CamelServlet.doService(CamelServlet.java:210)
at org.apache.camel.http.common.CamelServlet.service(CamelServlet.java:74)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:105)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:115)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:169)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:121)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:66)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)
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 org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:89)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:107)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:108)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:784)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:802)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1410)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.io.IOException: Stream closed
    at org.apache.catalina.connector.InputBuffer.read(InputBuffer.java:335)
    at org.apache.catalina.connector.CoyoteInputStream.read(CoyoteInputStream.java:155)
    at org.apache.camel.util.IOHelper.copy(IOHelper.java:196)
    at org.apache.camel.util.IOHelper.copy(IOHelper.java:169)
    at org.apache.camel.util.IOHelper.copyAndCloseInput(IOHelper.java:218)
    at org.apache.camel.util.IOHelper.copyAndCloseInput(IOHelper.java:214)
    at org.apache.camel.http.common.HttpHelper.readResponseBodyFromInputStream(HttpHelper.java:244)
    at org.apache.camel.http.common.HttpHelper.readRequestBodyFromServletRequest(HttpHelper.java:195)
    at org.apache.camel.http.common.DefaultHttpBinding.parseBody(DefaultHttpBinding.java:572)
    at org.apache.camel.http.common.HttpMessage.createBody(HttpMessage.java:72)
    ... 85 common frames omitted

Are there any experts out there that can tell me why this might stop working? I've checked to make sure the dependencies are compatible (Apache Camel 2.18, Spring Boot 1.4.1). I'm also letting Spring Boot (and camel-spring-boot) handle all of the auto-configuration for CamelServlet and CamelContext. Thanks

1
You sure the connection is not being closed for some reason? What happens if you post it from postman or something like that? - Souciance Eqdam Rashti

1 Answers

1
votes

In actuality, upgrading Camel appears to have exposed an implementation issue with my HttpErrorHandler. Once I redesigned the CamelRouter code block as follows, the problem went away:

// HTTP Error endpoints
// *** reimplemented HttpErrorHandler as a bean, now passing status codes as messages to be handled
from("{{camel.endpoint.my.servletEndpoint}}")
            .doTry()
                .process(myRequestProcessor)
                .to("{{camel.endpoint.my.trigger}}")
            .doCatch(IllegalArgumentException.class)
                .transform(constant(HttpStatus.BAD_REQUEST))
                .process(httpErrorHandler);
            .doCatch(IllegalStateException.class)
                .transform(constant(HttpStatus.INTERNAL_SERVER_ERROR))
                .process(httpErrorHandler);
            .end();

I can't say I completely understand why the original code stopped working (even if it worked badly :)) - perhaps it was attempting to reuse the incoming HTTP Exchange somehow - but the cleaner code seems to suffice.