1
votes

I'm trying to stream videos from the server's file system. I'm using StreamingResponseBody from Spring to be returned as a response. So I have to process streaming asynchronously. I did many research of how to configure my web.xml and Spring MVC and I followed this simple tutorial: http://shengwangi.blogspot.de/2015/09/asynchronous-spring-mvc-hello-world.html but unfortunately without success! I'm still getting exception telling me "Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding "true" to servlet and filter declarations in web.xml" despite I think that I did everything right. Here's my servlet configuration in web.xml

<servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/mvc-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/application-context.xml</param-value>
    </context-param>
    <context-param>
        <param-name>spring.profiles.active</param-name>
        <param-value>production</param-value>
    </context-param>
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>ASYNC</dispatcher>
    </filter-mapping>
    <filter>
        <display-name>springSecurityFilterChain</display-name>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>ASYNC</dispatcher>
    </filter-mapping>

The code snipt of the Spring MVC configuration:

<mvc:annotation-driven>
    <mvc:async-support default-timeout="30000"  task-executor="taskExecutor">
    </mvc:async-support>
</mvc:annotation-driven>
<bean id="taskExecutor"
        class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="5" />
    <property name="maxPoolSize" value="50" />
    <property name="queueCapacity" value="10" />
    <property name="keepAliveSeconds" value="120" />
</bean>

Here's the request handler:

@GetMapping("/videos/play")
    public StreamingResponseBody stream(@RequestParam("code") String code, @RequestParam("locale") String locale) throws FileNotFoundException{
        File video = videoService.getVideo(code, locale);
        final InputStream videoInputStream = new FileInputStream(video);
        return (outputStream) -> {
            copyToStream(videoInputStream, outputStream);
        };
    }
private void copyToStream(final InputStream is, OutputStream os)
            throws IOException {
        byte[] data = new byte[2048];
        int read = 0;
        while ((read = is.read(data)) > 0) {
            os.write(data, 0, read);
        }
        os.flush();
    }

I appreciate any help, since I'm struggling 3 days ago! I'm using Tomcat8.0 and Spring 4.3.6

1

1 Answers

2
votes

I read many threads which suggest that it is a bug in Tomcat 7 or 8, but it doesn't.The exception makes it clear that async-support must be enabled in ALL filters. Since I tried to do that, but the problem was how I added async support to the filters in web.xml file. What I did is the following

<filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>ASYNC</dispatcher>
</filter-mapping>

This actually tells that the mapping accept ASYNC requests (I guess), but it doesn't enable the async support in the filter itself. So in each filter definition we should enable async support, so the springSecurityFilterChain definition becomes:

<filter>
    <display-name>springSecurityFilterChain</display-name>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <async-supported>true</async-supported>
</filter>