Servlet 3 Async support with Spring MVC doesn't work - spring-mvc

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

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>

Related

Spring Integration : int-http:inbound-gateway no Path Variables set

I'm using Spring Integration's (version 4.3.1) int-http:inbound-gateway and I'm find #pathVariables aren't getting set.
<int-http:inbound-gateway id="httpInboundGatewayPathAdapterControlBus"
request-channel="controlBusIn"
reply-channel="controlBusWithReplyChannel"
supported-methods="GET"
path="{pathvalue}" request-payload-type="java.lang.String"
payload-expression="#pathVariables.pathvalue">
My web.xml
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<servlet>
<servlet-name>httpInboundGatewayPathAdapterControlBus</servlet-name>
<servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>httpInboundGatewayPathAdapterControlBus</servlet-name>
<url-pattern>/controlBusURIExpression/*</url-pattern>
</servlet-mapping>
Debugging seems to show that spring's HttpRequestHandlingEndpointSupport finds nothing set URI_TEMPLATE_VARIABLES_ATTRIBUTE below :-
Map<String, String> pathVariables =
(Map<String, String>) servletRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
I also tried adding
<bean id="integrationRequestMappingHandlerMapping" class="org.springframework.integration.http.inbound.IntegrationRequestMappingHandlerMapping"/>
But still get an Exception, I think because no path variables are being added in, and the stacktrace indicates that IntegrationRequestMappingHandlerMapping isn't being used.
Any help would be much appreciated.
2017-02-17 11:26:03.223:WARN:oejs.ServletHandler:qtp2505735-16: /xxxxxxxxx-integration-app/controlBusURIExpression/controlBus/getCreateCheckCaseCurrentConsumers
org.springframework.expression.spel.SpelEvaluationException: EL1007E:(pos 0): Property or field 'pathvalue' cannot be found on null
at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:220)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:94)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.access$000(PropertyOrFieldReference.java:46)
at org.springframework.expression.spel.ast.PropertyOrFieldReference$AccessorLValue.getValue(PropertyOrFieldReference.java:374)
at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:88)
at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:120)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:242)
at org.springframework.integration.http.inbound.HttpRequestHandlingEndpointSupport.actualDoHandleRequest(HttpRequestHandlingEndpointSupport.java:463)
at org.springframework.integration.http.inbound.HttpRequestHandlingEndpointSupport.doHandleRequest(HttpRequestHandlingEndpointSupport.java:399)
at org.springframework.integration.http.inbound.HttpRequestHandlingMessagingGateway.handleRequest(HttpRequestHandlingMessagingGateway.java:103)
at org.springframework.web.context.support.HttpRequestHandlerServlet.service(HttpRequestHandlerServlet.java:67)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:806)
at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1669)
I just tried it with the http sample app, changing the inbound gateway to
<int-http:inbound-gateway request-channel="receiveChannel"
path="{pathValue}"
payload-expression="#pathVariables.pathValue"
supported-methods="GET"/>
And it worked fine for me:
I can only get that error by mis-typing the variable name.
Your web.xml should be something like this...
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>http</display-name>
<servlet>
<servlet-name>SI</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/servlet-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SI</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>

Post a values with umlauts(äöü) to Cuba Portal App Spring MVC Controller

I have some with the binding in Spring MVC Controller (#ModelAttribute("name") String name)
#RequestMapping(value = "/authors", method = RequestMethod.POST)
public String index(
#ModelAttribute("name") String name) {...}
Post auf .../authors
Form data: name=Näöüme
In controller name:Näöüme
If i get the requests inputstream the name=N%C3%A4%C3%B6%C3%BCme is URLEncoded.
Is there a way to set the urlencoding?
I found the solution, I have to set the filterEncoding in web.xml:
<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>
</filter-mapping>

Spring MVC - Request method 'POST' not supported

I am learning Spring MVC (with Thymeleaf) while porting over a JBoss Seam website to Spring MVC.
I am trying to replace a HTTPServlet (doPost to /myservlet) with a Spring Controller with the following code:
#RequestMapping(value="/myservlet", method = RequestMethod.POST)
public String executeAction(HttpServletRequest request, HttpServletResponse response) throws IOException {
StringBuilder buffer = new StringBuilder();
BufferedReader reader = request.getReader();
String line;
while((line = reader.readLine()) != null) {
buffer.append(line);
}
String payload = buffer.toString();
System.out.println("payload: " + payload);
return "/index";
}
This method needs to read the XML Payload (String) sent via a HTTP Post to this endpoint.
When the external client (.NET - which will be used in the live environment) invokes this I get the following log messages:
[org.springframework.web.servlet.PageNotFound] (default task-3) Request method 'POST' not supported
[org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver] (default task-3) Handler execution resulted in exception: Request method 'POST' not supported
I have also tried this as a HTTPServlet but with the same problem. Can someone please advise as to what I am doing wrong?
The web.xml contents are:
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<error-page>
<error-code>404</error-code>
<location>/404.html</location>
</error-page>
<!-- Send unauthorised request to the 404 page -->
<error-page>
<error-code>403</error-code>
<location>/404.html</location>
</error-page>
<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/appServlet/servlet-context.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- Spring Security -->
<filter>
<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>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
</web-app>
The xml payload:
<Jobs>
<Job Action="Post">
<AdvertiserName>Advertiser1</AdvertiserName>
<AdvertiserType ValueID="15897">15897</AdvertiserType>
<SenderReference>01111111</SenderReference>
<DisplayReference>DISPLAYREF_635346301296069467_4445_Test89
</DisplayReference>
<Classification ValueID="6211">1002915</Classification>
<SubClassification></SubClassification>
<Position><![CDATA[CASE MANAGER]]></Position>
<Description><![CDATA[ The Case Manager role is the vital link between all parties within the mortgage application process. ...]]></Description>
<Country ValueID="246">United Kingdom</Country>
<Location ValueID="12096">Yorkshire</Location>
<Area ValueID="107646">Bradford</Area>
<PostalCode>BD1 1EE</PostalCode>
<ApplicationURL>http://removed.com/Application.aspx?uPjAaXJ9HmZ04+4i/bqmFAz
</ApplicationURL>
<Language ValueID="120036">2057</Language>
<ContactName>Joe Bloggs</ContactName>
<EmploymentType ValueID="2163">2163</EmploymentType>
<StartDate></StartDate>
<Duration></Duration>
<WorkHours ValueID="2190">2190</WorkHours>
<SalaryCurrency ValueID="1078">1007000</SalaryCurrency>
<SalaryMinimum>16200.00</SalaryMinimum>
<SalaryMaximum>16200.00</SalaryMaximum>
<SalaryPeriod ValueID="2178">1007600</SalaryPeriod>
<JobType>APPLICATION</JobType>
</Job>
</Jobs>
In case anyone else has this issue, the problem is related to the method signature and CSRF.
I got around this by following geoand's advice (thanks) by changing the method signature to add #RequestBody String payload
And by disabling CSRF for the specific URL (/myservlet) but leaving it enabled for the other URL's using the following in the spring security config:
<http auto-config="true" use-expressions="true" pattern="/myservlet" >
<csrf disabled="true"/>
</http>
<http auto-config="true" use-expressions="true" >
<access-denied-handler error-page="/403" />
<form-login login-page="/login.html" authentication-failure-url="/login-error.html" authentication-success-handler-ref="customAuthenticationSuccessHandler" />
<logout logout-success-url="/index" />
<intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" />
<intercept-url pattern="/advertiser/**" access="hasRole('ROLE_ADVERTISER')" />
<csrf disabled="false"/>
</http>
Thank you all for your replies/comments.
Kaz
I removed crsf
.and().csrf().and().exceptionHandling().accessDeniedPage("/Access_Denied");
I changed as
and().exceptionHandling().accessDeniedPage("/Access_Denied");//this Work

How to read spring-application-context.xml and AnnotationConfigWebApplicationContext both in spring mvc

In case I want to read bean definitions from spring-application-context.xml, I would do this in web.xml file.
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
In case I want to read bean definitions through Java Configuration Class (AnnotationConfigWebApplicationContext), I would do this in web.xml
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
org.package.MyConfigAnnotatedClass
</param-value>
</init-param>
</servlet>
How do I use both in my application. like reading beans from both configuration xml file and annotated class.
Is there a way to load spring beans in xml file while we are using AppConfigAnnotatedClass to instantiate/use rest of the beans.
This didnt work
Xml file defines bean as
<bean name="mybean" class="org.somepackage.MyBean"/>
Java Class Imports Resources as
#ImportResource(value = {"classpath:some-other-context.xml"})
#Configuration
public class MyConfigAnnotatedClass {
#Inject
MyBean mybean;
}
But mybean value is always null which ofcourse will give nullpointerexception when calling method on mybean.
You can annotate your #Configuration class with
#ImportResource(value = {"classpath:some-other-context.xml"})
#Configuration
public class MyConfigAnnotatedClass {
...
}
to have it import <beans> type xml contexts.
You can do the same thing the other way around. Your #Configuration class is also a #Component. If you have a <component-scan> that includes its package, all its declared beans will be added to the context. Alternatively, you can do
<bean name="myAdditionalConfig" class="org.somepackage.MyConfigAnnotatedClass" />
Note that package cannot be used as a name in the package structure.

Rest easy and init params - how to access?

I'd like to have some init params in my web.xml and retrieve them later in the application, I know I can do this when I have a normal servlet. However with resteasy I configure HttpServletDispatcher to be my default servlet so I'm not quite sure how I can access this from my rest resource. This might be completely simple or I might need to use a different approach, either way it would be good to know what you guys think. Following is my web.xml,
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>RestEasy sample Web Application</display-name>
<!-- <context-param>
<param-name>resteasy.scan</param-name>
<param-value>true</param-value>
</context-param> -->
<listener>
<listener-class>
org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
</listener-class>
</listener>
<servlet>
<servlet-name>Resteasy</servlet-name>
<servlet-class>
org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher
</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.pravin.sample.YoWorldApplication</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Resteasy</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
My question is how do I set something in the init-param and then retrieve it later in a restful resource. Any hints would be appreciated. Thanks guys!
Use the #Context annotation to inject whatever you want into your method:
#GET
public Response getWhatever(#Context ServletContext servletContext) {
String myParm = servletContext.getInitParameter("parmName");
}
With #Context you can inject HttpHeaders, UriInfo, Request, HttpServletRequest, HttpServletResponse, ServletConvig, ServletContext, SecurityContext.
Or anything else if you use this code:
public class MyApplication extends Application {
public MyApplication(#Context Dispatcher dispatcher) {
MyClass myInstance = new MyClass();
dispatcher.getDefautlContextObjects().
put(MyClass.class, myInstance);
}
}
#GET
public Response getWhatever(#Context MyClass myInstance) {
myInstance.doWhatever();
}

Resources