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,
Related
need your help here please.
I have a login page. After I enter username/password, I want to see Dashboard page. But I am getting 404 page not found. Can anyone please tell what is going on here.
When I hit http://localhost:8080 -> It goes to http://localhost:8080/login - Which is expected.
After I enter username/password, it goes to http://localhost:8080 - Expected: to go to Dashboard page i.e. http://localhost:8080/dashboard
#Component
public class SimpleAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
...
redirectStrategy.sendRedirect(request, response, "dashboard");
}
DashboardController.java
#Controller
#RequestMapping("/dashboard")
public class DashboardController {
#RequestMapping("/")
String init(){
System.out.println("Dashboard - init()");
return "dashboard_init";
}
}
app.component.html
Hello... {{name}}
Hello... {{title}}
<h1>
Welcome {{title}}!
</h1>
<p>Id: <span>{{greeting.id}}</span></p>
<p>Message: <span>{{greeting.content}}!</span></p>
app-routing.module.ts
import {DashboardComponent} from "./dashboard/dashboard.component";
const routes: Routes = [
{ path: '', redirectTo: '/login', pathMatch: 'full' },
// { path: 'dashboard', redirectTo: '/dashboard', pathMatch: 'full' },
{
path: 'dashboard_init',
component: DashboardComponent,
data: { title: 'Dashboard' }
}
];
dashboard.component.ts
#Component({
selector: 'dashboard-component',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css'],
})
export class DashboardComponent implements OnInit {
private currentAssociate: Associate;
constructor(private http: Http,
private router: Router) {
}
ngOnInit(): void {
// initialize services and data
this.http
.get('/dashboard')
.toPromise()
.then(response => {
let data = response.json();
if (data.currentAssociate) this.currentAssociate = data.currentAssociate as Associate;
})
.catch(error => {
// this.alertService.error(error);
});
}
}
dashboard.component.html
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="utf-8" />
<title>Dashboard</title>
</head>
<div>
<B>Dashboard...</B>
</div>
</html>
Error: (When the url is http://localhost:8080/dashboard/)
Dashboard - init()
[2m2018-03-26 10:07:20.421[0;39m [31mERROR[0;39m [35m13184[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36morg.thymeleaf.TemplateEngine [0;39m [2m:[0;39m [THYMELEAF][http-nio-8080-exec-2] Exception processing template "dashboard_init": Error resolving template "dashboard_init", template might not exist or might not be accessible by any of the configured Template Resolvers
[2m2018-03-26 10:07:20.422[0;39m [31mERROR[0;39m [35m13184[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.a.c.c.C.[.[.[/].[dispatcherServlet] [0;39m [2m:[0;39m Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: Error resolving template "dashboard_init", template might not exist or might not be accessible by any of the configured Template Resolvers] with root cause
org.thymeleaf.exceptions.TemplateInputException: Error resolving template "dashboard_init", template might not exist or might not be accessible by any of the configured Template Resolvers
at org.thymeleaf.TemplateRepository.getTemplate(TemplateRepository.java:246)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1104)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1060)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1011)
something wrong overall..
in Controller replace top /dashboard with /
#Controller
#RequestMapping("/")
public class DashboardController {
#RequestMapping("/")
String init(){
System.out.println("Dashboard - init()");
return "dashboard_init";
}
}
Also as fas as I remember return "dashboard_init" is expecting dashboard_init.html template to be returned
Probably you want redirect or something to /dashboard_init, do like
#RequestMapping(value = "/", method = RequestMethod.GET)
public void index(final HttpServletResponse response) {
response.setStatus(HttpStatus.OK.value());
response.sendRedirect( "/wherever-you-want");
}
This series of tutorials will help you to integrate Spring Security with Angular, using different approaches. Starting with Basic Auth
Spring Security and Angular
I think my configuration is ok but I keep getting the same error "Did not find handler method".
The error:
10:55:46.284 [http-nio-8080-exec-3] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Looking up handler method for path /WEB-INF/views/html/products/list.html
10:55:46.285 [http-nio-8080-exec-3] DEBUG org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Did not find handler method for [/WEB-INF/views/html/products/list.html]
10:55:46.285 [http-nio-8080-exec-3] WARN org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/store/WEB-INF/views/html/products/list.html] in DispatcherServlet with name 'spring-web'
10:55:46.285 [http-nio-8080-exec-3] DEBUG org.springframework.web.servlet.DispatcherServlet - Successfully completed request
My web.xml is as below:
<servlet>
<servlet-name>spring-web</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-web</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
My spring-web-servlet.xml
<context:component-scan base-package="com.ecommerce.config" />
My SprinWebConfig.java
#EnableWebMvc
#Configuration
#ComponentScan({ "com.ecommerce.web", "com.ecommerce.service", "com.ecommerce.dao", "com.ecommerce.exception", "com.ecommerce.validator" })
public class SpringWebConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Bean
public InternalResourceViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/html/");
viewResolver.setSuffix(".html");
return viewResolver;
}
#Bean
public ResourceBundleMessageSource messageSource() {
ResourceBundleMessageSource rb = new ResourceBundleMessageSource();
rb.setBasenames(new String[] {"messages/messages", "messages/validation"});
return rb;
}
}
ProductController.java
#Controller
public class ProductController {
private final Logger logger = LoggerFactory.getLogger(ProductController.class);
#GetMapping("/")
public String index(Model model) {
logger.debug("index()");
return "redirect:/products";
}
#GetMapping("/products")
public String showAllProducts(Model model) {
logger.debug("showAllProducts()");
//Here I'm constructing a list named products
model.addAttribute("products", products);
return "products/list";
}
}
So, I can't see where is my problem. Can you help please? thanks
I have an API REST Controller on my Spring 4 application that returns JSON values but somehow, looking at the logs, the dispatcher redirect the request to '/'.
2016-04-01 11:30:35 DEBUG RequestResponseBodyMethodProcessor:221 - Written [{"type":"groupmatch"}] as "application/json;charset=UTF-8" using [org.springframework.http.converter.StringHttpMessageConverter#4617ede3]
2016-04-01 11:30:35 DEBUG DispatcherServlet:1034 - Null ModelAndView returned to DispatcherServlet with name 'dispatcher': assuming HandlerAdapter completed request handling
2016-04-01 11:30:35 DEBUG DispatcherServlet:1000 - Successfully completed request
2016-04-01 11:30:35 DEBUG ExceptionTranslationFilter:115 - Chain processed normally
2016-04-01 11:30:35 DEBUG SecurityContextPersistenceFilter:97 - SecurityContextHolder now cleared, as request processing completed
2016-04-01 11:30:35 DEBUG AntPathRequestMatcher:145 - Checking match of request : '/'; against '/'
2016-04-01 11:30:35 DEBUG FilterChainProxy:180 - / has an empty filter list
2016-04-01 11:30:35 DEBUG DispatcherServlet:861 - DispatcherServlet with name 'dispatcher' processing GET request for [/]
2016-04-01 11:30:35 DEBUG RequestMappingHandlerMapping:319 - Looking up handler method for path /
2016-04-01 11:30:35 DEBUG RequestMappingHandlerMapping:326 - Returning handler method [public java.lang.String com.thebetcafe.controllers.HomeController.home(org.springframework.web.context.request.WebRequest)]
2016-04-01 11:30:35 DEBUG DefaultListableBeanFactory:248 - Returning cached instance of singleton bean 'homeController'
2016-04-01 11:30:35 DEBUG DispatcherServlet:947 - Last-Modified value for [/] is: -1
2016-04-01 11:30:35 DEBUG DispatcherServlet:1241 - Rendering view [org.springframework.web.servlet.view.JstlView: name 'static/home.html'; URL [static/home.html]] in DispatcherServlet with name 'dispatcher'
2016-04-01 11:30:35 DEBUG JstlView:166 - Forwarding to resource [static/home.html] in InternalResourceView 'static/home.html'
2016-04-01 11:30:35 DEBUG DispatcherServlet:861 - DispatcherServlet with name 'dispatcher' processing GET request for [/static/home.html]
2016-04-01 11:30:35 DEBUG RequestMappingHandlerMapping:319 - Looking up handler method for path /static/home.html
2016-04-01 11:30:35 DEBUG RequestMappingHandlerMapping:329 - Did not find handler method for [/static/home.html]
2016-04-01 11:30:35 DEBUG SimpleUrlHandlerMapping:191 - Matching patterns for request [/static/home.html] are [/static/**]
2016-04-01 11:30:35 DEBUG SimpleUrlHandlerMapping:220 - URI Template variables for request [/static/home.html] are {}
2016-04-01 11:30:35 DEBUG SimpleUrlHandlerMapping:141 - Mapping [/static/home.html] to HandlerExecutionChain with handler [ResourceHttpRequestHandler [locations=[ServletContext resource [/static/]], resolvers=[org.springframework.web.servlet.resource.PathResourceResolver#6a23a6d6]]] and 1 interceptor
2016-04-01 11:30:35 DEBUG DispatcherServlet:947 - Last-Modified value for [/static/home.html] is: -1
2016-04-01 11:30:35 DEBUG DispatcherServlet:1034 - Null ModelAndView returned to DispatcherServlet with name 'dispatcher': assuming HandlerAdapter completed request handling
2016-04-01 11:30:35 DEBUG DispatcherServlet:1000 - Successfully completed request
2016-04-01 11:30:35 DEBUG DispatcherServlet:1000 - Successfully completed request
I have a specific filtering to authenticate the user via a token in the header but I am not sure how it can be generated by that. Here is the code:
#RestController
#RequestMapping("/api")
public class APIController {
...
#RequestMapping(value="/groups",method = RequestMethod.GET, produces = "application/json; charset=UTF-8")
public #ResponseBody String getAllGroups(){
List<CompetitionGroup> groups = groupService.findAllGroups();
return groupListToJSONObject(groups);
}
...
}
dispatcher-servlet.xml
<!-- SCANNING THE COMPONENT -->
<context:component-scan base-package="com.myapp"/>
<context:annotation-config/>
<context:property-placeholder/>
<mvc:annotation-driven/>
<mvc:resources mapping="/static/**" location="/static/"/>
pom.xml
<!-- Jackson (JSON) -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>${jackson.version}</version>
</dependency>
I have implemented a specific filter for JWT token authentication below:
public class JWTTokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
#Autowired
private JWTUtil jwtUtil;
private static final Logger logger = Logger.getLogger(JWTTokenAuthenticationFilter.class);
public JWTTokenAuthenticationFilter(){
this("/api/");
}
public JWTTokenAuthenticationFilter(String defaultFilterProcessesUrl) {
super(defaultFilterProcessesUrl);
setAuthenticationManager(new NoOpAuthenticationManager());
setAuthenticationSuccessHandler(new JWTSuccessAuthenticationHandler());
}
public final String HEADER_SECURITY_TOKEN = "Authorization";
#Override
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
return true;
}
/**
* Attempt to authenticate request - basically just pass over to another method to authenticate request headers
*/
#Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
String header = request.getHeader(HEADER_SECURITY_TOKEN);
if (header == null || !header.startsWith("Bearer ")) {
throw new JwtTokenMissingException("No JWT token found in request headers");
}
/* Try to parse the token */
AppUser userDetails = jwtUtil.parseToken(header.substring(7));
Collection<SimpleGrantedAuthority> auth = new ArrayList<>();
auth.add(new SimpleGrantedAuthority(userDetails.getRole().name()));
JWTAuthenticationToken token = new JWTAuthenticationToken(userDetails.getEmail(), userDetails, auth);
token.setAuthenticated(true);
return token;
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
super.successfulAuthentication(request, response, chain, authResult);
// As this authentication is in HTTP header, after success we need to continue the request normally
// and return the response as if the resource was not secured at all
chain.doFilter(request, response);
}
}
The trick is that the AuthenticationSuccessHandler was redirecting the request to '/' when authenticating.
So I created a new JWTSuccessAuthenticationHandler who does nothing after the authentication.
public class JWTSuccessAuthenticationHandler implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
// We do not need to do anything extra on REST authentication success, because there is no page to redirect to
}
}
Hope that helps someone :)
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
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>