File Upload using Spring WebFlow 2.4.0, parameter not binded - java

I'm using Spring Framework 4.1.5, Spring Security 4.0.0.RC2, Spring Webflow 2.4.0.RELEASE and Tomcat 8.0.15.
I followed the example in the webflow documentation, but I can't get the file in my form bean.
The form
<form:form action="${flowExecutionUrl}" method="post" commandName="fileForm" enctype="multipart/form-data">
<form:input type="file" value="" path="multipartFileUpload"/>
<button type="submit" name="_eventId_forward"><spring:message code="signup.forward"/></button>
<sec:csrfInput/>
</form:form>
The form bean
public class FileForm implements Serializable {
private static final long serialVersionUID = 1L;
private transient MultipartFile multipartFileUpload;
public MultipartFile getMultipartFileUpload() {
return multipartFileUpload;
}
public void setMultipartFileUpload(final MultipartFile multipartFileUpload) {
this.multipartFileUpload = multipartFileUpload;
}
}
The flow
<view-state id="companyLogo" view="signup/company-logo" model="fileForm">
<var name="fileForm" class="it.openex.pmcommonw.form.FileForm"/>
<transition on="back" to="chooseProfile" bind="false" validate="false"/>
<transition on="forward" to="companyInfo">
<evaluate expression="userCommonBean.uploadImage(fileForm)"/>
</transition>
</view-state>
The backing object
#Component
public class UserCommonBean {
public static void uploadImage(final FileForm fileForm) throws IOException, ServletException {
fileForm.getMultipartFileUpload(); // always null!!!
}
}
The multipartResolver
#Bean
public CommonsMultipartResolver filterMultipartResolver() {
final CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setMaxUploadSize(10 * 1024 * 1024);
multipartResolver.setMaxInMemorySize(1048576);
multipartResolver.setDefaultEncoding("UTF-8");
return multipartResolver;
}
webflow configuration
#Configuration
public class WebFlowConfig extends AbstractFlowConfiguration {
#Autowired
TilesViewResolver viewResolver;
#Bean
public FlowDefinitionRegistry flowRegistry() {
return getFlowDefinitionRegistryBuilder()
.setFlowBuilderServices(flowBuilderServices())
.setBasePath("/WEB-INF/flows/")
.addFlowLocation("signup.xml", UrlMap.SIGNUP_WEBFLOW)
.addFlowLocation("user-edit.xml", UrlMap.PROFILE_EDIT_WEBFLOW)
.build();
}
#Bean
public FlowExecutor flowExecutor() {
return getFlowExecutorBuilder(flowRegistry()).build();
}
#Bean
public FlowHandlerAdapter flowHandlerAdapter() {
final FlowHandlerAdapter flowHandlerAdapter = new FlowHandlerAdapter();
flowHandlerAdapter.setFlowExecutor(flowExecutor());
return flowHandlerAdapter;
}
#Bean
public FlowHandlerMapping flowHandlerMapping() {
final FlowHandlerMapping flowHandlerMapping = new FlowHandlerMapping();
flowHandlerMapping.setFlowRegistry(flowRegistry());
// this has to be less than -1
flowHandlerMapping.setOrder(-2);
return flowHandlerMapping;
}
#Bean
public MvcViewFactoryCreator mvcViewFactoryCreator() {
final MvcViewFactoryCreator mvcViewFactoryCreator = new MvcViewFactoryCreator();
final List<ViewResolver> viewResolvers = Collections.singletonList(viewResolver);
mvcViewFactoryCreator.setViewResolvers(viewResolvers);
return mvcViewFactoryCreator;
}
#Bean
public FlowBuilderServices flowBuilderServices() {
return getFlowBuilderServicesBuilder().setViewFactoryCreator(mvcViewFactoryCreator())
.setValidator(localValidatorFactoryBean()).build();
}
#Bean
public LocalValidatorFactoryBean localValidatorFactoryBean() {
return new LocalValidatorFactoryBean();
}
}
Inside Tomcat's context.xml I already added allowCasualMultipartParsing="true"
Debugging the application I can see the file data inside the request, and I can get it if I try to post the form to a normal controller.
I tried also to remove Spring Security but it still didn't work inside Spring WebFlow.
In the requestParameters object there are only 3 objects:
execution
_eventid_forward
_csrf
There are some relevant rows in the logs
DEBUG 2015-03-13 18:03:15,053: org.springframework.web.multipart.support.MultipartFilter - Using MultipartResolver 'filterMultipartResolver' for MultipartFilter
DEBUG 2015-03-13 18:03:15,053: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'filterMultipartResolver'
DEBUG 2015-03-13 18:03:15,053: org.springframework.web.multipart.support.MultipartFilter - Resolving multipart request [/registrazione] with MultipartFilter
DEBUG 2015-03-13 18:03:15,060: org.springframework.web.multipart.commons.CommonsMultipartResolver - Found multipart file [multipartFileUpload] of size 469217 bytes with original filename [PoliziaMunicipale.png], stored in memory
....
DEBUG 2015-03-13 18:03:15,072: org.springframework.binding.mapping.impl.DefaultMapper - Beginning mapping between source [org.springframework.webflow.core.collection.LocalParameterMap] and target [it.openex.pmcommonw.form.FileForm]
DEBUG 2015-03-13 18:03:15,072: org.springframework.binding.mapping.impl.DefaultMapping - Adding mapping result [TargetAccessError#34bc31ea mapping = parameter:'execution' -> execution, code = 'propertyNotFound', error = true, errorCause = org.springframework.binding.expression.PropertyNotFoundException: Property not found, originalValue = 'e1s2', mappedValue = [null]]
DEBUG 2015-03-13 18:03:15,072: org.springframework.binding.mapping.impl.DefaultMapper - Completing mapping between source [org.springframework.webflow.core.collection.LocalParameterMap] and target [it.openex.pmcommonw.form.FileForm]; total mappings = 1; total errors = 1
The multipartFileUpload property is not binded in the FileForm bean.
I'm not sure if it's useful, but inside org.springframework.webflow.context.servlet.HttpServletRequestParameterMap at line 52
if (request instanceof MultipartHttpServletRequest) {
// ... process multipart data
}
it fails the check because the request is an instance of org.springframework.security.web.context.HttpSessionSecurityContextRepository$Servlet3SaveToSessionRequestWrapper
Update 1
I can confirm that multipartRequest.getFile("file") also works.
I can't enable the org.springframework.web.multipart.support.MultipartFilter filter though.
If it's enabled the multipartRequest is an instance of StandardMultipartHttpServletRequest containing a Servlet3SecurityContextHolderAwareRequestWrapper, wrapping a Servlet3SaveToSessionRequestWrapper, finally containing an unreachable DefaultMultipartHttpServletRequest with the multipartFile I need, but I can't get it.
Disabling it I'm able to get it because multipartRequest became an instance of DefaultMultipartHttpServletRequest, but there's no file validation and the maxUploadSize limit of CommonsMultipartResolver is not respected.
Plus if Tomcat launches an exception because the file is too big for Tomcat's maxPostSize limit, the exception is caught by my CustomAccessDeniedHandler because its type is org.springframework.security.access.AccessDeniedException, and the error message is Invalid CSRF Token 'null' was found on the request parameter '_csrf' or header 'X-CSRF-TOKEN'..
Looking at the request object I can see the original Tomcat exception org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException. It seems like there's nothing to handle it properly, but, as I said, if I enable the MultipartFilter I can't get the file.

We ran into the same problems, since we use Spring Security 4.xx in our web application.
The Problem is that a org.springframework.security.web.context.HttpSessionSecurityContextRepository$Servlet3SaveToSessionRequestWrapper isn't instance of org.springframework.web.multipart.MultipartHttpServletRequest but it contains one. A cast to won't work and ClassCastException will occur.
Thats the reason why
if (request instanceof MultipartHttpServletRequest) {
// ... process multipart data
}
never can be true.
The idea was to create a org.springframework.web.multipart.support.StandardMultipartHttpServletRequest from the native HttpServletRequest and it works.
In our WebApp we use Pojo Actions indicated in Spring Webflow documentation Section 6.5.1. Invoking a POJO action.
Our Workaround:
PojoAction.java
public String fileUpload(RequestContext requestContext) {
final ServletExternalContext context = (ServletExternalContext) requestContext.getExternalContext();
final MultipartHttpServletRequest multipartRequest = new StandardMultipartHttpServletRequest((HttpServletRequest)context.getNativeRequest());
final File file = multipartRequest.getFile("file");
fileUploadHandler.processFile(file); //do something with the submitted file
}
In flow.xml we have an action state like this:
<action-state id="upload-action">
<evaluate expression="pojoAction.uploadFile(flowRequestContext)"/>
<transition to="show"/>
</action-state>
In this case the binding to a model is not needed.
I hope it helps!
According to Update 1
In web.xml the CSRF-Protection Filter must declared before SpringSecurityFilterChain.
In our application the web.xml looks like this
<filter>
<filter-name>csrfFilter</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>csrfFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<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>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>

Related

Unable to upload file to Spring MVC with Spring Security despite full configuration

I'm trying to upload .pdf file with jQuery AJAX to Spring MVC 5 with Spring Security 5 back-end running on Tomcat and faced multiple issues depending on Spring configuration
NOTE:
File upload should be available without authentication
Front-end
Markup:
<div id="upload-modal" class="modal">
<div class="modal-content">
<h4>Upload</h4>
<form action="#" enctype="multipart/form-data">
<div class="file-field input-field">
<div class="btn">
<span>View...</span>
<input type="file" name="file" accept="application/pdf">
</div>
<div class="file-path-wrapper">
<label>
<input class="file-path validate" type="text">
</label>
</div>
</div>
</form>
</div>
<div class="modal-footer">
Cancel
Upload
</div>
</div>
csrf header for all the requests:
$(document).ready(function () {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function (e, xhr, options) {
xhr.setRequestHeader(header, token);
});
});
Uploading with jQuery AJAX:
$("#upload-bttn").click(function () {
var $uploadModal = $("#upload-modal");
const fileName = $uploadModal.find(".file-path").val();
const extension = fileName.substr(fileName.lastIndexOf(".") + 1);
if (extension === "pdf") {
$.ajax({
url: "/upload",
type: "POST",
data: new FormData($uploadModal.find("form").get(0)),
processData: false,
contentType: false,
success: function () {
console.log("success")
},
error: function () {
console.log("error")
}
});
} else {
M.toast({html: 'Selected file is not .pdf'});
}
});
Back-end
General configuration looks like below. It is modified depending on the cases
Security Initialization:
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
public SecurityInitializer() {
super(SecurityContext.class);
}
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
}
Application initialization:
public class ApplicationInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) {
servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE));
servletContext.getSessionCookieConfig().setHttpOnly(true);
servletContext.getSessionCookieConfig().setSecure(true);
AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext();
dispatcherServlet.register(WebAppContext.class);
ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(dispatcherServlet));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
}
}
Case 1 - CommonsMultipartResolver bean definition
CommonsMultipartResolver bean definition:
#Bean
public CommonsMultipartResolver multipartResolver(
#Value("${max.upload.size}") Integer maxNumber,
#Value("${max.size}") Integer maxSize) {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(1024 * maxSize * maxNumber);
resolver.setMaxUploadSizePerFile(maxSize);
resolver.setMaxInMemorySize(maxSize);
resolver.setDefaultEncoding("UTF-8");
try {
resolver.setUploadTempDir(new FileSystemResource(System.getProperty("java.io.tmpdir")));
} catch (IOException e) {
e.printStackTrace();
}
return resolver;
}
I remember there was strange Spring behavior when MultipartResolver bean should be named "multipartResolver" explicitly. I tried both #Bean and #Bean("multipartResolver") with configuration above and had same result (despite bean above is named "multipartResolver" as per method name)
Result:
Error 500 - Unable to process parts as no multi-part configuration has been provided
Case 2 - MultipartConfigElement in Servlet registry
removed CommonsMultipartResolver bean
added StandardServletMultipartResolver bean
added MultipartConfigElement to ApplicationInitializer
StandardServletMultipartResolver bean definition:
#Bean
public StandardServletMultipartResolver multipartResolver() {
return new StandardServletMultipartResolver();
}
Updated ApplicationInitializer:
#Override
public void onStartup(ServletContext servletContext) {
...
servlet.setMultipartConfig(new MultipartConfigElement(
System.getProperty("java.io.tmpdir")
));
}
As per Spring documentation:
Ensure that the MultipartFilter is specified before the Spring Security filter. Specifying the MultipartFilter after the Spring Security filter means that there is no authorization for invoking the MultipartFilter which means anyone can place temporary files on your server. However, only authorized users will be able to submit a File that is processed by your application
As I need to allow not authenticated users to upload the files I tried both before and after in SecurityInitializer as below with the same result
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
or
#Override
protected void afterSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
Result:
Error 403
Questions
What do I miss in the configuration?
Thoughts
CommonsMultipartResolver would be preferable as allows to drive it with Spring properties
Something wrong with Spring Security context setup
There is allowCasualMultipartParsing="true" option (did not test) which I wouldn't like to stick to as its Tomcat specific
Updates
With disabled Spring Security everything works properly
http.authorizeRequests().antMatchers("/**").permitAll(); remains as the only security context configuration so don't think its Security context configuration issue
Set multipart resolver bean name explicitly in MultipartFilter in
beforeSpringSecurityFilterChain(ServletContext servletContext) and still no luck
Adding of _csrf token to the request header did not work for both cases
Realized that I miss additional WebAppContext class in SecurityInitializer constructor. Now error 500 disappeared but 403 appeared for case 1. Logging says that I have invalid csrf token despite I added it to the header like above
Tried to submit the form with csrf token including hidden input <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> yet the result is the same - error 403 with invalid token statement
After two days of struggling:
Constructor should contain both security and application context configuration classes
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
public SecurityInitializer() {
super(SecurityContext.class, WebAppContext.class);
}
}
Application context (WebAppContext) should contain MultipartResolver bean definition
#Bean
public CommonsMultipartResolver multipartResolver(
#Value("${max.upload.size}") Integer maxNumber,
#Value("${max.size}") Integer maxSize) {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(1024 * maxSize * maxNumber);
resolver.setMaxUploadSizePerFile(maxSize);
resolver.setMaxInMemorySize(maxSize);
resolver.setDefaultEncoding("UTF-8");
try {
resolver.setUploadTempDir(new FileSystemResource(System.getProperty("java.io.tmpdir")));
} catch (IOException e) {
e.printStackTrace();
}
return resolver;
}
In my case after application initialization csrf token inside Spring CsrfTokenRepository was empty for some reason so when Spring been comparing token from client request header with null in CsrfFilter Spring was returning error 403. I configured csrf in security context in the following way:
#Override
protected void configure(HttpSecurity http) throws Exception {
...
http.csrf().csrfTokenRepository(new CookieCsrfTokenRepository());
...
}
Now csrf token is passed in cookies with first server response to the browser and the repository generates and caches a token to compare against the one coming from the client so comparison passes successfully
Here CookieCsrfTokenRepository may also be declared as CookieCsrfTokenRepository.withHttpOnlyFalse() if you would like to grab the token from cookie and set it into csrf header, but I have chosen to go with meta tags approach above

Spring 4 - Resources mapping - Did not find handler method

I know that there are many topics with this problem because I've gone over all of them. But still I haven't found any solution.
Basically I have a ResourceHandler that maps a resource but doesn't find the css file while entering the jsp. I'm using Spring 4.3.9.RELEASE.
The project look like this
Web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>mvc-dispatcher</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>
com.res.context.MvcConfig
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
Configuration file looks like this:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"com.res"})
public class MvcConfig extends WebMvcConfigurerAdapter {
//I've already tried without this
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
super.configurePathMatch(configurer);
configurer.setUseRegisteredSuffixPatternMatch(false);
configurer.setUseSuffixPatternMatch(false);
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
registry.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
// TODO Auto-generated method stub
configurer.enable();
}
#Bean
public InternalResourceViewResolver getInternalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
}
The jsp file
I've already tried many values of href
//Those two give 404 error code
<link rel="stylesheet" href="resources/style.css" type="text/css">
<link rel="stylesheet" href="/resources/style.css" type="text/css">
//Those two give jsp fatal error with NullPointerException
<link rel="stylesheet" href='<c:uri>value="/resources/style.css"</c:uri> 'type="text/css">
<link rel="stylesheet" href='<c:uri>value="resources/style.css"</c:uri> 'type="text/css">
Controller
#Controller
#RequestMapping("/")
public class MyController {
private static final Logger logger = Logger.getLogger(MyController.class);
#RequestMapping("/site")
public ModelAndView site()
{
ModelAndView model = new ModelAndView("site");
model.addObject("txt", "Model");
return model;
}}
And finally most important part
logs
2017-07-18 20:56:35 DEBUG DispatcherServlet:869 - DispatcherServlet with name 'mvc-dispatcher' processing GET request for [/res/site.htm]
2017-07-18 20:56:35 DEBUG RequestMappingHandlerMapping:310 - Looking up handler method for path /site.htm
2017-07-18 20:56:35 DEBUG RequestMappingHandlerMapping:320 - Did not find handler method for [/site.htm]
2017-07-18 20:56:35 DEBUG RequestMappingHandlerMapping:310 - Looking up handler method for path /site.htm
2017-07-18 20:56:35 DEBUG RequestMappingHandlerMapping:317 - Returning handler method [public org.springframework.web.servlet.ModelAndView com.res.controller.MyController.site()]
2017-07-18 20:56:35 DEBUG DefaultListableBeanFactory:251 - Returning cached instance of singleton bean 'myController'
2017-07-18 20:56:35 DEBUG DispatcherServlet:955 - Last-Modified value for [/res/site.htm] is: -1
2017-07-18 20:56:35 DEBUG DefaultListableBeanFactory:1670 - Invoking afterPropertiesSet() on bean with name 'site'
2017-07-18 20:56:35 DEBUG DispatcherServlet:1280 - Rendering view [org.springframework.web.servlet.view.JstlView: name 'site'; URL [/WEB-INF/views/site.jsp]] in DispatcherServlet with name 'mvc-dispatcher'
2017-07-18 20:56:35 DEBUG JstlView:432 - Added model object 'txt' of type [java.lang.String] to request in view with name 'site'
2017-07-18 20:56:35 DEBUG JstlView:166 - Forwarding to resource [/WEB-INF/views/site.jsp] in InternalResourceView 'site'
2017-07-18 20:56:36 DEBUG DispatcherServlet:1000 - Successfully completed request
2017-07-18 20:56:36 DEBUG DispatcherServlet:869 - DispatcherServlet with name 'mvc-dispatcher' processing GET request for [/res/resources/style.css]
2017-07-18 20:56:36 DEBUG RequestMappingHandlerMapping:310 - Looking up handler method for path /resources/style.css
2017-07-18 20:56:36 DEBUG RequestMappingHandlerMapping:320 - Did not find handler method for [/resources/style.css]
2017-07-18 20:56:36 DEBUG RequestMappingHandlerMapping:310 - Looking up handler method for path /resources/style.css
2017-07-18 20:56:36 DEBUG RequestMappingHandlerMapping:320 - Did not find handler method for [/resources/style.css]
2017-07-18 20:56:36 DEBUG SimpleUrlHandlerMapping:192 - Matching patterns for request [/resources/style.css] are [/resources/**]
2017-07-18 20:56:36 DEBUG SimpleUrlHandlerMapping:226 - URI Template variables for request [/resources/style.css] are {}
2017-07-18 20:56:36 DEBUG SimpleUrlHandlerMapping:140 - Mapping [/resources/style.css] to HandlerExecutionChain with handler [ResourceHttpRequestHandler [locations=[ServletContext resource [/resources/]], resolvers=[org.springframework.web.servlet.resource.PathResourceResolver#4c531172]]] and 1 interceptor
2017-07-18 20:56:36 DEBUG DispatcherServlet:955 - Last-Modified value for [/res/resources/style.css] is: -1
2017-07-18 20:56:36 DEBUG DispatcherServlet:1048 - Null ModelAndView returned to DispatcherServlet with name 'mvc-dispatcher': assuming HandlerAdapter completed request handling
2017-07-18 20:56:36 DEBUG DispatcherServlet:1000 - Successfully completed request
I hope that someone knows what is wrong here because after few hours I can't think of anything more.
The problem is because you have the folder resources out of webapp and your servlet is looking at webapp as /.
First way:
Move your resources folder under webapp and then into your applicationContext.xml add the next line:
<mvc:resources mapping="/css/**" location="/resources/" />
then you can import your style.css file like this:
<link href="<c:url value="/resources/style.css" />" rel="stylesheet">
Second way:
you can create another Servlet for static content:
First add the next few lines in your web.xml
<servlet>
<servlet-name>resources</servlet-name>
<servlet-class>com.res.servlets.DefaultServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>resources</servlet-name>
<url-pattern>/resources/*</url-pattern>
</servlet-mapping>
next step, create the package com.res.servlet the under that package create the class DefaultServlet like this:
public class DefaultServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
// Tomcat, Jetty, JBoss, and GlassFish
private static final String COMMON_DEFAULT_SERVLET_NAME = "default";
// Resin
private static final String RESIN_DEFAULT_SERVLET_NAME = "resin-file";
// WebLogic
private static final String WEBLOGIC_DEFAULT_SERVLET_NAME = "FileServlet";
// WebSphere
private static final String WEBSPHERE_DEFAULT_SERVLET_NAME = "SimpleFileServlet";
public String scanDefaultServlet(){
if(this.getServletContext().getNamedDispatcher(COMMON_DEFAULT_SERVLET_NAME) != null) {
return COMMON_DEFAULT_SERVLET_NAME;
} else if(this.getServletContext().getNamedDispatcher(RESIN_DEFAULT_SERVLET_NAME) != null) {
return RESIN_DEFAULT_SERVLET_NAME;
} else if(this.getServletContext().getNamedDispatcher(WEBLOGIC_DEFAULT_SERVLET_NAME) != null){
return WEBLOGIC_DEFAULT_SERVLET_NAME;
} else if(this.getServletContext().getNamedDispatcher(WEBSPHERE_DEFAULT_SERVLET_NAME) != null){
return WEBSPHERE_DEFAULT_SERVLET_NAME;
} else {
throw new IllegalStateException("Cannot determine what Server you currently use");
}
}
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
RequestDispatcher rd = getServletContext().getNamedDispatcher(this.scanDefaultServlet());
HttpServletRequest wrapped = new HttpServletRequestWrapper(req) {
public String getServletPath() {return "";}
};
rd.forward(wrapped, resp);
}
}
now you can call your resources like this:
<link rel="stylesheet" type="text/css" href="<c:url value="/resources/resources/style.css"/>">
Make sure you has imported the tag libs like this at the top of your view:
<%#taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
Regards,

Using Spring Mvc WebApplicationInitializer, But No mapping found for HTTP request

I am setup a sample Code base using Spring MVC in Eclipse and JBoss 6.2.
But I get '404' with http://localhost:8080/rest/simple/main
Jboss log as below:
2015-07-29 11:51:27,356 ERROR [controller.simpleController] (http-/0.0.0.0:8080-1) get request
2015-07-29 11:51:27,391 WARN [org.springframework.web.servlet.PageNotFound] (http-/0.0.0.0:8080-1) No mapping found for HTTP request with URI [/rest/WEB-INF/views/main.jsp] in DispatcherServlet with name 'dispatcher'
Directory :
>rest-server-simple
> -src
> -main
-java
-config
-InitConfig.java
-ServletConfig.java
-controller
-simpleController.java
> -webapp
> -WEB-INF
-jboss-web.xml
>views
-main.jsp
InitConfig:
public class InitConfig implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(ServletConfig.class);
ServletRegistration.Dynamic registration = servletContext.addServlet("dispatcher",new DispatcherServlet(ctx));
registration.setLoadOnStartup(1);
registration.addMapping("/*"); }}
ServletConfig:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages ="controller")
public class ServletConfig {
#Bean
public InternalResourceViewResolver internalResourceViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
jboss-web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<context-root>/rest</context-root>
</jboss-web>
simpleController:
#Controller
#RequestMapping(value = "/simple")
public class simpleController {
private static final Logger logger = LoggerFactory.getLogger(simpleController.class);
#RequestMapping(value = "/main", method = RequestMethod.GET)
public String hello(){
logger.error("get request");
return "main";
}
}
registration.addMapping("/*");
Change it to
registration.addMapping("/");
There is a difference between /* and / .
/* indicates that every request will be handled by DispatcherServlet, in this case retrieval of a jsp or anything like .../abc.xyz etc will also be forwarded to Dispatcher, so when controller requests for a view it actually looks for RequestMapping mapped for /WEB-INF/views/main.jsp but
/ tells container that only those requests that do not have pathinfo i.e /rest/simple/main will be handled by DispatcherServlet.
UPDATE#1
Hmm.. What I have found that jboss AS 7 doesn't like overriding default servlet i.e. / without web.xml and hence you are still getting 404 and not even getting anything on the logger, Reason being simple is that Dispatcher is never mapped to any url. If you want to check that just add following after addMapping("/*");
System.out.println("registration.getMappings() = " + registration.getMappings());
It works fine with Tomcat >= 7.0.15 or WildFly have checked on both.
To make it work on JBoss7 there are few options:
1. Change DispatcherServlet mapping from / to *.htm or something except DefaultServlet Mapping.
2. Switch your Configuration to web.xml. You will have to initialize DispatcherServlet there and pass Annotated class as `contextConfigLocation. Check here for REF

Spring Boot, Java Config - No mapping found for HTTP request with URI [/...] in DispatcherServlet with name 'dispatcherServlet'

This has been a quite common problem here in stackOverflow, but none of the topics of the same problem solves mine.
We have a template configuration that uses xml config, but now we're trying to move away from that and start using Java config.
So I have a new project using Java config and Spring Boot. We're also using JSP and Tiles 3.
Problem is: it fails to render our admin login page.
Here is the code:
Main config class:
#SpringBootApplication
#EnableScheduling
#Import(OnAdminBeans.class)
public class AppConfig extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(AppConfig.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(AppConfig.class);
}
}
The AppConfig.class is is the main package. Through the #ComponentScan that #SpringBootApplication brings, it scans the other configurations that are on mainpackage.config, so it imports the view config class:
#Configuration
#EnableWebMvc
public class ViewConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/adm/static/**").addResourceLocations("/adm/static/");
}
// #Override
// public void addViewControllers(ViewControllerRegistry registry) {
// registry.addViewController("/adm/login").setViewName("login-template-tiles");
// }
#Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.viewResolver(viewResolver());
registry.viewResolver(jspViewResolver());
registry.viewResolver(tilesViewResolver());
}
#Bean
public LocaleResolver localeResolver() {
CookieLocaleResolver localeResolver = new CookieLocaleResolver();
localeResolver.setCookieName("locale");
localeResolver.setCookieMaxAge(30);
localeResolver.setDefaultLocale(new Locale("pt", "BR"));
return localeResolver;
}
#Bean
public MultipleViewResolver viewResolver() {
Map<String, ViewResolver> viewsResolvers = new HashMap<String, ViewResolver>();
viewsResolvers.put(MultipleViewResolver.ViewType.JSP.getKey(), jspViewResolver());
viewsResolvers.put(MultipleViewResolver.ViewType.TILES.getKey(), tilesViewResolver());
MultipleViewResolver viewResolver = new MultipleViewResolver();
viewResolver.setViewsResolvers(viewsResolvers);
viewResolver.setOrder(1);
return viewResolver;
}
#Bean
public InternalResourceViewResolver jspViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp");
viewResolver.setViewClass(JstlView.class);
viewResolver.setOrder(2);
return viewResolver;
}
#Bean
public UrlBasedViewResolver tilesViewResolver() {
UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
viewResolver.setViewClass(TilesView.class);
viewResolver.setOrder(3);
return viewResolver;
}
#Bean
public TilesConfigurer tilesConfigurer() {
TilesConfigurer configurer = new TilesConfigurer();
configurer.setDefinitions("/WEB-INF/tile-defs/tiles-definitions.xml");
return configurer;
}
}
The LoginController.class is defined as:
#Controller
#RequestMapping(value = "/adm")
public class LoginController {
#RequestMapping(value = "/login")
public ModelAndView login() {
return new ModelAndView("login-template-tiles");
}
}
And in tiles-definitions.xml I have the following definition for login-template-tiles:
<definition name="login-template-tiles" template="/WEB-INF/jsp/adm/templates/login-template.jsp">
<put-attribute name="admin-title" value="Admin" />
<put-attribute name="content" value="/WEB-INF/jsp/adm/templates/sections/login/index.jsp" />
</definition>
Note that both files exist.
Given all that the LoginController.login() does get called when i try to access /adm/login. But it fails to find the proper jsp file, aparently.
It returns a 404. With TRACE enabled, I get the following log:
DispatcherServlet with name 'dispatcherServlet' processing GET request for [/WEB-INF/jsp/adm/templates/login-template.jsp]
Testing handler map [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#2118c09a] in DispatcherServlet with name 'dispatcherServlet'
Looking up handler method for path /WEB-INF/jsp/adm/templates/login-template.jsp
Did not find handler method for [/WEB-INF/jsp/adm/templates/login-template.jsp]
Testing handler map [org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping#2c148974] in DispatcherServlet with name 'dispatcherServlet'
No handler mapping found for [/WEB-INF/jsp/adm/templates/login-template.jsp]
Testing handler map [org.springframework.web.servlet.handler.SimpleUrlHandlerMapping#784c3547] in DispatcherServlet with name 'dispatcherServlet'
No handler mapping found for [/WEB-INF/jsp/adm/templates/login-template.jsp]
Testing handler map [org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport$EmptyHandlerMapping#533e0604] in DispatcherServlet with name 'dispatcherServlet'
Testing handler map [org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport$EmptyHandlerMapping#cfd1b4e] in DispatcherServlet with name 'dispatcherServlet'
No mapping found for HTTP request with URI [/WEB-INF/jsp/adm/templates/login-template.jsp] in DispatcherServlet with name 'dispatcherServlet'
Any suggestions are appreciated!
EDIT:
Ok. By debugging, I found out that it has something to do with the embedded Tomcat. Other than that, I have no clue what is going on.
EDIT 2:
Found that the problem is in org.springframework.web.servlet.DispatcherServlet#getHandler. It simply doesn't find a HandlerMapping for that request. Do I have to register one?
OK! Found the problem.
This link helped me: https://samerabdelkafi.wordpress.com/2014/08/03/spring-mvc-full-java-based-config/
More specifically this configuration:
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
By setting a default handler, I would no longer get a white page but instead the JSP code as html, which clearly tells me that the JSP was being found but not rendered.
So the answer was on this page: JSP file not rendering in Spring Boot web application
I was missing the tomcat-embed-jasper artifact.
Add below dependency to your pom.xml
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
The tips here helped me when I got stuck with a similar problem. I fixed it after adding this fragment on my configuration
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}

Unable to process parts as no multi-part configuration has been provided

I wrote a simple controller for uploading files:
#RestEndpoint
public class ImageController {
#Autowired
GridFsTemplate mTemplate;
#RequestMapping(value = "images", method = RequestMethod.POST)
public #ResponseBody String testPhoto(#RequestParam String name, #RequestParam String directory, #RequestParam MultipartFile file) throws IOException {
if(!file.isEmpty()){
final byte[] bytes = file.getBytes();
InputStream inputStream = new ByteArrayInputStream(bytes);
mTemplate.store(inputStream, "name");
return "uploaded photo";
}
return "failed";
}
}
#RestEndpoint annotation is:
#Target({ ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Controller
public #interface RestEndpoint
{
String value() default "";
}
My ContextCOnfiguration class is:
#Configuration
#EnableWebMvc
#ComponentScan(
basePackages = "com.questter.site",
useDefaultFilters = false,
includeFilters =
#ComponentScan.Filter({RestEndpoint.class, RestEndpointAdvice.class})
)
public class RestServletContextConfiguration extends WebMvcConfigurerAdapter {
#Bean
public CommonsMultipartResolver multiPartResolver(){
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
return resolver;
}
...
}
--- UPDATED ---
web.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>Spring Application</display-name>
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspf</url-pattern>
<page-encoding>UTF-8</page-encoding>
<scripting-invalid>true</scripting-invalid>
<include-prelude>/WEB-INF/jsp/base.jspf</include-prelude>
<trim-directive-whitespaces>true</trim-directive-whitespaces>
<default-content-type>text/html</default-content-type>
</jsp-property-group>
</jsp-config>
<!--<context-param>-->
<!--<param-name>spring.profiles.active</param-name>-->
<!--<param-value>development</param-value>-->
<!--</context-param>-->
<session-config>
<session-timeout>30</session-timeout>
<cookie-config>
<http-only>true</http-only>
</cookie-config>
<tracking-mode>COOKIE</tracking-mode>
</session-config>
<distributable />
</web-app>
---- UPDATED ----
public class Bootstrap implements WebApplicationInitializer
{
#Override
public void onStartup(ServletContext container) throws ServletException
{
container.getServletRegistration("default").addMapping("/resource/*");
AnnotationConfigWebApplicationContext rootContext =
new AnnotationConfigWebApplicationContext();
rootContext.register(RootContextConfiguration.class);
container.addListener(new ContextLoaderListener(rootContext));
AnnotationConfigWebApplicationContext webContext =
new AnnotationConfigWebApplicationContext();
webContext.register(WebServletContextConfiguration.class);
ServletRegistration.Dynamic dispatcher = container.addServlet(
"springWebDispatcher", new DispatcherServlet(webContext)
);
dispatcher.setLoadOnStartup(1);
dispatcher.setMultipartConfig(new MultipartConfigElement(
null, 20_971_520L, 41_943_040L, 512_000
));
dispatcher.addMapping("/");
AnnotationConfigWebApplicationContext restContext =
new AnnotationConfigWebApplicationContext();
restContext.register(RestServletContextConfiguration.class);
DispatcherServlet servlet = new DispatcherServlet(restContext);
servlet.setDispatchOptionsRequest(true);
dispatcher = container.addServlet(
"springRestDispatcher", servlet
);
dispatcher.setLoadOnStartup(2);
dispatcher.addMapping("/rest/*");
rootContext.refresh();
DbBootstrap dbBootstrap = rootContext.getBean(DbBootstrap.class);
dbBootstrap.init();
}
}
When perfoming a post request (using postman) i'm getting:
HTTP Status 500 - Request processing failed; nested exception is java.lang.IllegalArgumentException:Expected MultipartHttpServletRequest: is a MultipartResolver configured
I've looked over some similar questions over stackoverflow but none of the answers helped
me.
Spring version is: 4.0.4
Any help will be greatly appreciated (with a thumbs up of course).
Thanks
I don't know why they did this, but the MultipartResolver bean in the context needs to be named multipartResolver. Rename your #Bean method to
public CommonsMultipartResolver multipartResolver(){ // lowercase 'P'
Or give it the name explicitly
#Bean(name = "multipartResolver")
public CommonsMultipartResolver canBeCalledAnything(){
allowCasualMultipartParsing="true"
on context tag inside context.xml, it's work for me
It is straight forward from the exception that no multi-part configuration is found. Though you have provided multipartResolver bean.
The problem is that while specifying the MultipartFilter before the Spring Security filter, It tries to get the multipartResolver bean but can't find it. Because it expect the bean name/id as filterMultipartResolver instead of multipartResolver.
Do yourself a favor. Please change the bean configuration like following -
#Bean
public CommonsMultipartResolver filterMultipartResolver(){
CommonsMultipartResolver resolver = new
CommonsMultipartResolver();
return resolver;
}
or
#Bean(name = "filterMultipartResolver")
public CommonsMultipartResolver multiPartResolver(){
CommonsMultipartResolver resolver = new
CommonsMultipartResolver();
return resolver;
}
The answer by R. Ali Ashik worked for me.
Following is the relevant part of pom.xml of the project that I am working on:
<properties>
<springframework.version>5.0.2.RELEASE</springframework.version>
<springsecurity.version>5.0.0.RELEASE</springsecurity.version>
<hibernate.version>5.2.17.Final</hibernate.version>
<mysql.connector.version>8.0.11</mysql.connector.version>
Since, I have a custom login page with persistent authentication setup, I also needed to have the following:
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
}
But the actual clincher was this as pointed out by R. Ali Ashik:
#Bean(name = "filterMultipartResolver")
public CommonsMultipartResolver multiPartResolver(){
CommonsMultipartResolver resolver = new
CommonsMultipartResolver();
return resolver;
}
The relevant reference material in the context is this:
Class MultipartFilter
And the relevant text is as follows:
Looks up the MultipartResolver in Spring's root web application context. Supports a "multipartResolverBeanName" filter init-param in web.xml; the default bean name is "filterMultipartResolver". Looks up the MultipartResolver on each request, to avoid initialization order issues (when using ContextLoaderServlet, the root application context will get initialized after this filter).

Categories