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).
Related
I'm trying to convert my spring xml configuration to a java type configuration. So far I have gotten most things working but I am struggling with configuring the security of the application. I currently get the following error when trying to access a secured path.
Request processing failed; nested exception is org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
I assume I have missed something important but I don't understand what it is. Any help is appreciated.
Here are my configuration files.
SecurityConfiguration.java
#Configuration
#EnableWebSecurity
#Order(1)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
#Autowired
private NegotiateSecurityFilterEntryPoint authenticationEntryPoint;
#Autowired
private NegotiateSecurityFilter securityFilter;
#Bean
public WindowsAuthProviderImpl waffleAuthProvider(){
return new WindowsAuthProviderImpl();
}
#Bean(name="negotiateSecurityFilterProvider")
#Autowired
public NegotiateSecurityFilterProvider negotiateSecurityFilterProvider(
final WindowsAuthProviderImpl windowsAuthProvider
){
NegotiateSecurityFilterProvider bean = new NegotiateSecurityFilterProvider(windowsAuthProvider);
List<String> protocols = new ArrayList<>();
protocols.add("Negotiate");
bean.setProtocols(protocols);
return bean;
}
#Bean
public BasicSecurityFilterProvider basicSecurityFilterProvider(final WindowsAuthProviderImpl waffleAuthProvider){
return new BasicSecurityFilterProvider(waffleAuthProvider);
}
#Bean(name="waffleSecurityFilterProviderCollection")
#Autowired
public waffle.servlet.spi.SecurityFilterProviderCollection negotiateSecurityFilterProvider(
final BasicSecurityFilterProvider basicSecurityFilterProvider,
final NegotiateSecurityFilterProvider negotiateSecurityFilterProvider
) {
final List<SecurityFilterProvider> lsp = new ArrayList<>();
lsp.add(negotiateSecurityFilterProvider);
lsp.add(basicSecurityFilterProvider);
return new waffle.servlet.spi.SecurityFilterProviderCollection(lsp.toArray(new SecurityFilterProvider[]{}));
}
#Bean(name="negotiateSecurityFilterEntryPoint")
#Autowired
public waffle.spring.NegotiateSecurityFilterEntryPoint negotiateSecurityFilterEntryPoint(final SecurityFilterProviderCollection securityFilterProviderCollection) {
final waffle.spring.NegotiateSecurityFilterEntryPoint ep = new waffle.spring.NegotiateSecurityFilterEntryPoint();
ep.setProvider(securityFilterProviderCollection);
return ep;
}
#Bean(name="negotiateSecurityFilter")
#Autowired
public waffle.spring.NegotiateSecurityFilter waffleNegotiateSecurityFilter(
final SecurityFilterProviderCollection securityFilterProviderCollection
){
waffle.spring.NegotiateSecurityFilter bean = new waffle.spring.NegotiateSecurityFilter();
bean.setRoleFormat("both");
bean.setPrincipalFormat("fqn");
bean.setAllowGuestLogin(false);
bean.setProvider(securityFilterProviderCollection);
return bean;
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/assets/**");
web.ignoring().antMatchers("/img/**");
web.ignoring().antMatchers("/css/**");
web.ignoring().antMatchers("/partials/**");
web.ignoring().antMatchers("/error/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// A user needs to have the role user and has to be authenticated
Filter customNegotiateSecurityFilter = new CustomPreAuthSecurityFilter();
Filter customPreAuthSecurityFilter = new CustomNegotiateSecurityFilter();
http
.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
.addFilterBefore(customPreAuthSecurityFilter, BasicAuthenticationFilter.class)
.addFilterAfter(securityFilter, BasicAuthenticationFilter.class)
.addFilterAfter(customNegotiateSecurityFilter, BasicAuthenticationFilter.class)
.authorizeRequests().anyRequest().fullyAuthenticated();
}
}
SpringConfiguration.java
#Configuration
#EnableWebMvc
#EnableScheduling
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, proxyTargetClass = true)
#ComponentScan(basePackages = {"com.realitylabs.spa"})
#PropertySources({
#PropertySource("classpath:app.properties"),
#PropertySource("classpath:general.properties"),
#PropertySource("classpath:log4j.properties"),
#PropertySource("classpath:setup/app.properties"),
#PropertySource("classpath:setup/wro.properties"),
#PropertySource("classpath:setup/jdbc.properties"),
#PropertySource("file:${CONFIG_DIR}/config.properties")
})
public class SpringConfiguration extends WebMvcConfigurerAdapter {
#Autowired
Environment env;
#Autowired
SpringPostConstructHook sp;
private static final int CACHE_PERIOD = 31556926; // one year
// Configure static routes
#Override
public void addResourceHandlers(ResourceHandlerRegistry reg){
reg.addResourceHandler("/img/**")
.addResourceLocations("/assets/img/")
.setCachePeriod(CACHE_PERIOD);
reg.addResourceHandler("/assets/" + env.getProperty("VERSION") + "/**")
.addResourceLocations("/assets/")
.setCachePeriod(CACHE_PERIOD);
}
// Configuration variables
long MAX_UPLOAD_SIZE_IN_BYTES = 5000000;
// WRO4J config (js and css merge and minification)
#Bean
public PropertiesFactoryBean propertiesFactoryBean() {
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
Resource wroResource = new ClassPathResource("wro.properties");
propertiesFactoryBean.setLocation(wroResource);
return propertiesFactoryBean;
}
#Bean(name = "wroFilter")
public ConfigurableWroFilter getConfigurableWroFilter() throws IOException {
ConfigurableWroFilter filter = new ConfigurableWroFilter();
filter.setProperties(propertiesFactoryBean().getObject());
return filter;
}
// Post construction print outs
#Bean
public SpringPostConstructHook postConstructHook(){
return new SpringPostConstructHook();
}
// JSP view rendering
#Bean
public InternalResourceViewResolver jspViewResolver() {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setViewClass(org.springframework.web.servlet.view.JstlView.class);
bean.setPrefix("/WEB-INF/views/");
bean.setSuffix(".jsp");
bean.setOrder(1);
return bean;
}
// Handle file uploads
#Bean(name = "multipartResolver")
public CommonsMultipartResolver getMultipartResolver() {
CommonsMultipartResolver bean = new CommonsMultipartResolver();
bean.setMaxUploadSize(MAX_UPLOAD_SIZE_IN_BYTES);
return bean;
}
// Set specific hibernate config for postgres.
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper om = new HibernateAwareObjectMapper();
jsonConverter.setObjectMapper(om);
return jsonConverter;
}
// Poolable connection.
#Bean
public DataSource getDataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("jdbc_driverClassName"));
dataSource.setUrl(env.getProperty("jdbc_databaseurl"));
dataSource.setUsername(env.getProperty("jdbc_username"));
dataSource.setPassword(env.getProperty("jdbc_password"));
return dataSource;
}
#Bean
public DataSourceConnectionFactory getDataSourceConnectionFactory() {
DataSourceConnectionFactory bean = new DataSourceConnectionFactory(getDataSource());
return bean;
}
#Bean
public GenericObjectPool getPool() {
GenericObjectPool bean = new GenericObjectPool();
bean.setMaxIdle(30000);
bean.setTimeBetweenEvictionRunsMillis(60000);
return bean;
}
#Bean(name = "poolableConnectionFactory")
public PoolableConnectionFactory getPoolableConnectionFactory() {
PoolableConnectionFactory bean = new PoolableConnectionFactory(getDataSourceConnectionFactory(),getPool(),null,null,false,true);
return bean;
}
#Bean(name = "pooledDS")
#DependsOn({"poolableConnectionFactory"})
public PoolingDataSource getPoolingDataSource() {
PoolingDataSource bean = new PoolingDataSource(getPool());
return bean;
}
// Configure flyway database migrations
#Bean(name = "flyWay")
public Flyway getFlyWay(){
Flyway bean = new Flyway();
bean.setDataSource(getPoolingDataSource());
bean.setLocations("classpath:db/migrations");
bean.setValidateOnMigrate(false);
bean.migrate();
return bean;
}
// Configure hibernate ORM
#Bean(name = "sessionFactory")
#DependsOn({"flyWay"})
public LocalSessionFactoryBean sessionFactory(){
LocalSessionFactoryBean bean = new LocalSessionFactoryBean();
bean.setDataSource(getPoolingDataSource());
bean.setAnnotatedPackages("com.realitylabs.spa.entity");
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("jdbc_dialect"));
hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.showSQL"));
hibernateProperties.setProperty("hibernate.jdbc.batch_size", "30");
hibernateProperties.setProperty("hibernate.order_inserts", "true");
hibernateProperties.setProperty("hibernate.order_updates", "true");
hibernateProperties.setProperty("hibernate.connection.pool_size", "30");
bean.setHibernateProperties(hibernateProperties);
bean.setDataSource(getPoolingDataSource());
return bean;
}
// Configure scheduled jobs
#Bean(destroyMethod="shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(10);
}
#Scheduled(cron = "0 1 3 * * ?")
public void runSGCSJob() {
SGCSJob job = new SGCSJob();
try {
job.updateEmployees();
} catch (Exception e) {
e.printStackTrace();
}
}
#Scheduled(cron = "0 1 2 * * ?")
public void runLimeJob() {
LimeJob job = new LimeJob();
try {
job.updateCourses();
} catch (Exception e) {
e.printStackTrace();
}
}
// Print config and start jobs post construction
#PostConstruct
#DependsOn("sessionFactory")
public void init() throws SQLException, FileNotFoundException {
sp.printConfiguration();
sp.runJobs();
}
}
CacheConfiguration.java
#Configuration
#EnableCaching
public class CacheConfiguration implements CachingConfigurer {
// Replace the cache manager with ehcache
#Bean
#Override
public org.springframework.cache.CacheManager cacheManager() {
return new EhCacheCacheManager(getEhCacheFactory().getObject());
}
#Override
public CacheResolver cacheResolver() {
return new SimpleCacheResolver();
}
#Override
public KeyGenerator keyGenerator() {
return new SimpleKeyGenerator();
}
#Override
public CacheErrorHandler errorHandler() {
return new SimpleCacheErrorHandler();
}
// Configure ehcache
private EhCacheManagerFactoryBean getEhCacheFactory(){
EhCacheManagerFactoryBean factoryBean = new EhCacheManagerFactoryBean();
factoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
factoryBean.setShared(true);
return factoryBean;
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="MyCareerID" version="3.0">
<display-name>RDS</display-name>
<!-- Configure the session -->
<session-config>
<session-timeout>45</session-timeout>
<tracking-mode>COOKIE</tracking-mode>
</session-config>
<!-- Annotation config -->
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<!-- Context config location -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.realitylabs.spa.springConfig.SpringConfiguration</param-value>
</context-param>
<!-- Force UTF-8 encoding-->
<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>
<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Expose the security filter for the tests -->
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<!-- WRO4J configuration -->
<filter>
<filter-name>WebResourceOptimizer</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>wroFilter</param-value>
</init-param>
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<!-- Mapping wro for delivery of combined js files -->
<filter-mapping>
<filter-name>WebResourceOptimizer</filter-name>
<url-pattern>/assets/wro/*</url-pattern>
</filter-mapping>
<!-- Escape html sent to the server -->
<context-param>
<param-name>defaultHtmlEscape</param-name>
<param-value>true</param-value>
</context-param>
<!-- The container running spring -->
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Mapping root route to spring -->
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- Mapping error pages -->
<error-page>
<error-code>401</error-code>
<location>/WEB-INF/jsp/error.jsp</location>
</error-page>
</web-app>
I'm not certain if any other files might be interesting please ask if you need anything else.
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
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();
}
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>
I am trying to use 2 controllers with one dispatcher servlet in Spring MVC. But I am running into 404 errors when trying to render the views. The dispatcher is pretty straightforward, from web.xml:
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
and with the following configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
...
<context:component-scan base-package="com.mycompany.azalea" />
<mvc:annotation-driven />
</beans>
The controllers are:
package com.mycompany.azalea;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
#RequestMapping(value = "/home")
public class homeController {
#RequestMapping(value = "/")
public String home() {
return "index";
}
}
and
package com.mycompany.azalea;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
#RequestMapping(value = "/data")
public class dataController {
#RequestMapping(value = "/")
public String home() {
return "index";
}
}
and I am using a pretty standard resolver:
#Configuration
public class AppConfig {
// Resolve logical view names to .jsp resources in the /WEB-INF/views directory
#Bean
ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
}
Views are set up under WEB-INF/views/home/ and WEB-INF/views/data/
However if I try to request a URL like http://localhost:8080/Azalea/home/
I get an entry in the GlassFish log:
SEVERE: PWC6117: File ".../build/web/home/WEB-INF/views/index.jsp" not found
instead of the expected request for
".../build/web/WEB-INF/views/home/index.jsp"
Same pattern for "/data". It essentially looks like the request mapping is inserted into the wrong position in the request.
My current work around is to modify the resolver to
resolver.setPrefix("../WEB-INF/views/");
and return the following from the controller:
public class homeController {
#RequestMapping(value = "/")
public String home() {
return "home/index";
}
}
But this seems to be a suboptimal solution. Please let me know if you have any suggestions.
You almost got it right. The prefix has to be absolute here to make it work the way you want it to. That is: The prefix for the view-resolver has to be set as an absolute:
resolver.setPrefix("WEB-INF/views/");
And, when you return the view names from the #RequestMapping methods, they have to be the paths relative to your view-resolver's prefix path. So, in the homeController, you should return home/index, and in your dataController, you should return data/index.