I'm working on a POC using Spring with Annotation based configuration. While working on it, I'm facing below issues:
When I am using dispatcher servlet mapping as /, I am able to access the controllers but not html page.
When I change the mapping to /**, then I am able to access the html page but not the controllers.
I am not sure if I should add another dispatcher servlet and add one mapping in it. I also tried passing both the mappings in the dispatcher servlet, but it didn't work.
Maybe someone can help me with the issue.
Below is the code:
AppConfig
package com.upload.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
#Configuration
#Import(WebConfig.class)
#ComponentScan(basePackages= "com.upload")
public class AppConfig {
}
WebConfig
package com.upload.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter{
}
ServletInitializer
public class ServletInitializer extends AbstractDispatcherServletInitializer {
#Override
protected WebApplicationContext createServletApplicationContext() {
final AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(AppConfig.class);
context.getEnvironment().setActiveProfiles("prod");
return context;
}
#Override
protected String[] getServletMappings() {
return new String[] {"/"};
}
#Override
protected WebApplicationContext createRootApplicationContext() {
return null;
}
}
FileUploadController
#Controller
#RequestMapping("/ws")
public class FileUploadController {
#RequestMapping(value="hello",method=RequestMethod.GET)
public String hello(){
return "Hello";
}
}
Use Following Approach:
You need to add addResourceHandler to pick up the html page from the web-inf structure WEB-INF/views/viewer where all your pages like: js/ html /css/ images etc are present. You can remove your AppConfig .java shown above.
package com.Configuration.si.config;
#Configuration
#ComponentScan("com.Configuration.si")
#EnableWebMvc
public class Configuration extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/viewer/**","/lib/**","/js/**","/images/**","/css/**","/swf/**")
.addResourceLocations("/WEB-INF/views/viewer/","/WEB-INF/views/lib/","/WEB-INF/views/js/","/WEB-INF/views/images/","/WEB-INF/views/css/","/WEB-INF/views/swf/")
.setCachePeriod(315569126);
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
Also Add this class to your structure
package com.Configuration.si.config;
#Configuration
public class WebMvcConfiguration {
#Bean
public InternalResourceViewResolver internalResourceViewResolver() {
InternalResourceViewResolver internalResourceViewResolver = new InternalResourceViewResolver();
internalResourceViewResolver.setViewClass(JstlView.class);
internalResourceViewResolver.setPrefix("/WEB-INF/views/");
internalResourceViewResolver.setSuffix(".jsp");
return internalResourceViewResolver;
}
}
Internal view resolver will resolve the page you need using model.setViewName("login"); in controller. Hope it will help you
Related
I wrote a spring boot project.
It has three files.
Appconfig.java
package config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
#Configuration
#EnableWebMvc
#ComponentScan
(basePackages = {"controller"})
public class AppConfig {
}
ServletInitilizer.java
package config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class ServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[0];
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{AppConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
HelloController.java
package controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
public class HelloController {
#RequestMapping("hi")
#ResponseBody
public String hi() {
return "Hello, world.";
}
}
When I try to run it, it has error "No mapping found for HTTP request with URI [/SpringC1_01/] in DispatcherServlet with name 'dispatcher'".
Is this because server didn't find the controller or other reason? Thx.
Yes. i suspect two issues in the code.
#SpringBootApplication annotation is missing in AppConfig.
#RestController annotation is missing in HelloController.
Most of all you are missing a couple of things here.
Main class which contains public static void main and this class should be annotated with #SpringBootApplication
HelloController should be annotated with #RestController
At method level it should definitely point to some HTTP method in your case perhaprs it is Get mapping, so add #GetMapping annotation arround the method.
Move RequestMapping annotation from method level and add it to HelloController class.
RESOLVED - see answer.
I've looked through many similar questions and don't see a similar case right off. Certainly this isn't a unique situation and I'm just missing it?
Update A Spring example I found shows a priority property that may help here, but I have only found the XML example. Question expanded below.
Problem Summary
Two view resolvers appear to be conflicting in my SpringWebMVC application.
Problem Details
I'm work on a web app using Spring 4.0.3-RELEASE and have recently added Jackson to support returning Json from calls to a specific controller. This was working until I added an #Override to my SpringWebConfig for configureViewResolvers. Now my calls to my controller which was serving Json just return the template name which should call the Jackson mapper bean.
The big question
How can I make these two coexist? I have found that I can call:
registry.order(int)
and set it to 9 just to make sure it was last, but it still intercepted the jsonTemplate response from the controller. I don't see a way to set an order for the MappingJackson2JsonView bean. #Bean(order=0), for example, is invalid.
Things Tried
Redacting the ViewResolverRegistry, as expected, produces an error when trying to get mapped jsp views.
javax.servlet.ServletException: Could not resolve view with name 'someView' in servlet with name 'spring-mvc-dispatcher'
As noted in the question statement above, I've tried setting the order on the registry for the ViewResolverRegistry, but this did not help.
I also have tried adding the following to the MappingJackson2JsonView instance, view:
Properties props = new Properties();
props.put("order", 1);
view.setAttributes(props);
But as before, this doesn't prevent the ViewResolverRegistry from intercepting "jsonTemplate" before the Jackson mapper can process it.
I also have changed the load order of the configs in the AppInitializer, the code below has been updated to reflect the new load order, but this also did not help.
Reading through the Spring documentation a bit more, it appears that adding a ContentNegotiationConfigurer is going to be what I need to resolve this and I'm presently looking at how to get this to work in a way that preserves auto mapping the Model returned to the jsonTemplate view. Exapmles I've seen so far use a jsp as a view with specific properties called out, which defeats the purpose of using a Json Mapper.
Configuration
I have multiple config classes defined in my package com.mytest.config.
AppInitializer.java handles adding the *config classes to the context.
package com.mytest.config;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
public class AppInitializer implements WebApplicationInitializer {
private Logger logger = LoggerFactory.getLogger(AppInitializer.class);
#Override
public void onStartup(ServletContext container) throws ServletException {
try {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(JSONConfiguration.class);
ctx.register(SpringWebConfig.class);
ctx.setServletContext(container);
container.addListener(new ContextLoaderListener(ctx));
container.addListener(new RequestContextListener());
logger.info("Created AnnotationConfigWebApplicationContext");
ServletRegistration.Dynamic dispatcher = container.addServlet("spring-mvc-dispatcher", new DispatcherServlet(ctx));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
logger.info("DispatcherServlet added to AnnotationConfigWebApplicationContext");
} catch (Exception e) {
logger.error(e.getLocalizedMessage(), e);
}
}
}
SpringWebConfig.java is where I register the majority of my beans.
package com.mytest.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
#Configuration
#EnableWebMvc
#ComponentScan(basePackages={"com.mytest.controller","com.mytest.bean","com.mytest.model"})
#PropertySource(value={"classpath:application.properties"})
public class SpringWebConfig extends WebMvcConfigurerAdapter {
#Autowired
private Environment env;
private Logger logger = LoggerFactory.getLogger(SpringWebConfig.class);
// bunches of beans such as JdbcTemplate, DataSource... omitted for simplicity
#Override // apparent problem location -- needed for jsp resolving
public void configureViewResolvers(final ViewResolverRegistry registry) {
registry.jsp("/WEB-INF/views/html/",".jsp");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
logger.info("DefaultServletHandlerConfigurer enabled");
}
#Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(new com.honda.hrao.rid.config.RequestInterceptor());
logger.info("RequestInterceptor added to InterceptorRegistry");
}
}
JSONConfiguration.java is a controller I set up just for JSON.
package com.mytest.config;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.BeanNameViewResolver;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
#Configuration
#ComponentScan(basePackages = {"com.mytest.controller"})
#EnableWebMvc
public class JSONConfiguration {
private Logger logger = LoggerFactory.getLogger(JSONConfiguration.class);
#Bean // needed for JSON conversion of bean responses
public View jsonTemplate() {
logger.info("Registered MappingJackson2JsonView");
MappingJackson2JsonView view = new MappingJackson2JsonView();
Properties props = new Properties();
props.put("order", 1);
view.setAttributes(props);
view.setPrettyPrint(true);
return view;
}
#Bean
public ViewResolver viewResolver() {
logger.info("Starting ViewResolver bean");
return new BeanNameViewResolver();
}
}
Implementation
In my Controller, the following method should return JSON.
#Autowired
AppConstants appConstants;
#RequestMapping(method = RequestMethod.GET, value = "getAppConstants")
public String getAppConstants(Model model) {
model.addAttribute("AppConstants",appConstants);
if(appConstants==null) {
Logger.error("appConstants not autowired!!!");
return null;
}
return "jsonTemplate";
}
As mentioned above in Things Tried, this works fine if I remove the ViewResolverRegistry bean from the SpringWebConfig and if I leave the bean in place, the above controller method returns
404, /WEB-INF/views/html/jsonTemplate.jsp
The requested resource is not available.
-- which I understand. That's what the view resolver should do. How do I make my JSON calls bypass this?
It turns out there were only a couple of things missing. The first was to add the following annotation to the bean declaration for the mapper:
#Primary
So now, the bean setup looks like this.
#Bean // needed for JSON conversion of bean responses
#Primary
public View jsonTemplate() {
logger.info("Registered MappingJackson2JsonView");
MappingJackson2JsonView view = new MappingJackson2JsonView();
Properties props = new Properties();
props.put("order", 1);
view.setAttributes(props);
view.setPrettyPrint(true);
return view;
}
The second was to use a ContentNegotiationConfigurer. In my SpringWebConfig, I added the following:
public void configurationContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer
.ignoreUnknownPathExtensions(false)
.defaultContentType(MediaType.TEXT_HTML);
}
and changed my configureViewResolvers function as follows:
#Override // needed for jsp resolving
public void configureViewResolvers(final ViewResolverRegistry registry) {
MappingJackson2JsonView view = new MappingJackson2JsonView();
view.setPrettyPrint(true);
registry.enableContentNegotiation(view);
registry.jsp("/WEB-INF/views/html/",".jsp");
}
One clue was found in this example. The rest came from the Spring documentation.
I have search a lot but I did not find answer to my question, So I am posting my question here. Please look and suggest me the solution where I am mistaken.
I have created spring boot web mvc project with thymeleaf support using Spring Tool Suite(STS). When I run it give me "Whitelabel Error Page" page. Which means mapping not found.
Efforts:
WebConfig.java
package com.springthymeleaf.config;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
#Configuration
#ComponentScan("com.springthymeleaf")
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
ServletRegistrationBean servletRegistration(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean();
registrationBean.addUrlMappings("/console/*");
return registrationBean;
}
//start Thymeleaf specific configuration
#Bean(name ="templateResolver")
public ServletContextTemplateResolver getTemplateResolver() {
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver();
// templateResolver.setPrefix("/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("XHTML");
return templateResolver;
}
#Bean(name ="templateEngine")
public SpringTemplateEngine getTemplateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(getTemplateResolver());
return templateEngine;
}
#Bean(name="viewResolver")
public ThymeleafViewResolver getViewResolver(){
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(getTemplateEngine());
return viewResolver;
}
//end Thymeleaf specific configuration
#Bean(name ="messageSource")
public MessageSource getMessageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("/WEB-INF/i18/thymeleafResource");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
}
SecurityConfiguration.java
package com.springthymeleaf.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests().antMatchers("/").permitAll();
}
}
ServletInitializer.java
package com.springthymeleaf;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
public class ServletInitializer extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SpringThymeLeafApplication.class);
}
}
SpringThymeLeafApplication.java
package com.springthymeleaf;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class SpringThymeLeafApplication {
public static void main(String[] args) {
SpringApplication.run(SpringThymeLeafApplication.class, args);
}
}
IndexController.java
package com.springthymeleaf.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
public class IndexController {
#RequestMapping("/")
public String index(){
return "index";
}
}
I have created index.html file in resources/templates folder. Still I am getting that error. I have searched a lot on web, but did not get clue. Please somebody help me.
Actually Spring Boot configures Thymeleaf out of the box. It should work with the following setup:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login").defaultSuccessUrl("/").permitAll() // http://docs.spring.io/spring-security/site/docs/4.0.3.RELEASE/reference/htmlsingle/#jc-form
.and()
.logout().permitAll(); // http://docs.spring.io/spring-security/site/docs/4.0.3.RELEASE/reference/htmlsingle/#jc-logout
}
#Override
public void configure(WebSecurity web) throws Exception
{
web
.ignoring()
.antMatchers("/resources/**"/*, ... */);
}
}
#Controller
public class LoginController
{
#RequestMapping("/login")
static String login(Model model)
{
return "login";
}
}
Spring Boot already configures Thymeleaf for you, so no need to configure that manually. Remove all Thymeleaf related configuration, also remove #EnableWebMvc as that interferes with the Spring Boot auto configuration. The #ComponentScan is also redundant.
Spring Boot also registered a MessageSource for you so no need to configure that. Not sure what the servlet registration is you do but that is the only thing you need.
Also I suggest to remove your controller and use a view controller which you can configure in your WebConfig class. Saves you a controller.
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
ServletRegistrationBean servletRegistration(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean();
registrationBean.addUrlMappings("/console/*");
return registrationBean;
}
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
}
To let the auto configured message source pickup your custom bundles add the following to src/main/resources/application.properties.
spring.messages.basename=/WEB-INF/i18/thymeleafResource
I would also suggest to simply let the SpringThymeLeafApplication extend the SpringBootServletInitializer.
#SpringBootApplication
public class SpringThymeLeafApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(SpringThymeLeafApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SpringThymeLeafApplication.class);
}
}
Also make sure that your templates are in src/main/resources/templates and not in src/main/resources/resources/templates else those will not be found.
Spring boot does all the automatic configuration when you add the thymeleaf dependency. then you should do the following.
Remove all the thymeleaf configuration you have on your WebConfig.java
Make sure you have the following dependency your pom.xml if you are using Maven, otherwise check the spring website for the equivalent if you are using gradle:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
thirdly you make sure that you scanning where your controllers are, add the following on your SpringThymeLeafApplication.java:
#ComponentScan(basePackages = "your.path.to.controllers")
Finally you have to add your .html files to resources/templates
I'm trying to add security to my Spring 3.2.8-based pure Java-configured application. I'm following the instructions http://docs.spring.io/spring-security/site/docs/3.2.2.RELEASE/reference/htmlsingle/#jc
I've completed section 3.1, and the documentation says at this point that every URL should require authentication, but none do (at least, I can load every URL). It says it creates a Servlet filter, etc.
It's evident that by itself, that WebSecurityConfigurerAdapter subclass is not enough. So I look at section 3.1.1, which says the next step is to register the springSecurityFilterChain with the WAR, and goes on to say how in a Servlet 3+ environment, I need to subclass AbstractSecurityWebApplicationInitializer. But I'm already subclassing AbstractAnnotationConfigDispatcherServletInitializer. Am I supposed to have one of each? There is some discussion of ordering in the AbstractSecurityWebApplicationInitializer JavaDoc, implying I should have more than one initializer class.
In all this, it also said to add the WebSecurityConfigurerAdapter subclass to getRootConfigClasses() (although the example doesn't show the "AppConfig" that the other Spring getting started docs have you create; also, this alone wasn't enough).
So I tried adding another initializer class. All my other classes are public static inner classes of my AbstractAnnotationConfigDispatcherServletInitializer subclass, so I put another in there to be the AbstractSecurityWebApplicationInitializer subclass (rather than creating a separate .java file).
WARNING com.caucho.server.webapp.WebApp setConfigException: java.lang.UnsupportedOperationException: unimplemented
at com.caucho.server.webapp.ServletContextImpl.setSessionTrackingModes(ServletContextImpl.java:552)
at org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer.onStartup(AbstractSecurityWebApplicationInitializer.java:120)
at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:174)
at com.caucho.server.webapp.WebApp.callInitializer(WebApp.java:3471)
at com.caucho.server.webapp.WebApp.callInitializers(WebApp.java:3439)
at com.caucho.server.webapp.WebApp.startImpl(WebApp.java:3661)
at com.caucho.server.webapp.WebApp$StartupTask.run(WebApp.java:5196)
at com.caucho.env.thread2.ResinThread2.runTasks(ResinThread2.java:173)
at com.caucho.env.thread2.ResinThread2.run(ResinThread2.java:118)
I tried adding ordering to no avail. My entire config:
package com.latencyzero.satdb.web;
//
// Java Imports
//
import java.util.Properties;
import java.util.ResourceBundle;
import javax.naming.InitialContext;
import javax.servlet.ServletContext;
import javax.sql.DataSource;
//
// Library Imports
//
import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.xml.DOMConfigurator;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
//
// Project Imports
//
/**
There are still some things that get configured in the container. This app
was developed using Resin. Things configured in resin's XML config for this
app include the data source, error page, key store password for Apple
Push Notifications (which should move to the DB).
*/
public
class
WebappInitializer
extends
org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer
{
#Override
protected
Class<?>[]
getRootConfigClasses()
{
Class<?>[] classes = { AppConfig.class, SecurityConfig.class };
return classes;
}
#Override
protected
Class<?>[]
getServletConfigClasses()
{
Class<?>[] classes = { WebConfig.class };
return classes;
}
#Override
protected
java.lang.String[]
getServletMappings()
{
String[] mappings = { "/" };
return mappings;
}
#Override
protected
javax.servlet.Filter[]
getServletFilters()
{
return new javax.servlet.Filter[]
{
new org.springframework.orm.hibernate3.support.OpenSessionInViewFilter(),
};
}
/** *******************************************************************************************************************
App context configuration.
Hibernate config (data access, transactions, data model).
*/
#Configuration
#EnableAsync
#EnableTransactionManagement
#ComponentScan(basePackages = { "com.mymodelanddao", })
public
static
class
AppConfig
{
#Bean
public
org.springframework.jndi.JndiObjectFactoryBean
dataSource()
{
org.springframework.jndi.JndiObjectFactoryBean bean = new org.springframework.jndi.JndiObjectFactoryBean();
bean.setJndiName("java:comp/env/jdbc/db");
return bean;
}
#Bean
public
org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean
sessionFactory()
{
DataSource dataSource = (DataSource) dataSource().getObject();
org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean bean = new org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean();
bean.setDataSource(dataSource);
// TODO: Do we need to scan this, since it's done as part of #ComponentScan?
bean.setPackagesToScan(new String[] {"com.latencyzero.satdb.model"});
Properties props = new Properties();
props.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
props.setProperty("hibernate.cache.provider_class", "org.hibernate.cache.NoCacheProvider");
props.setProperty("hibernate.jdbc.batch_size", "200");
props.setProperty("hibernate.show_sql", "false");
props.setProperty("hibernate.format_sql", "true");
props.setProperty("hibernate.use_sql_comments", "false");
props.setProperty("hibernate.generate_statistics", "true");
bean.setHibernateProperties(props);
return bean;
}
#Bean
public
PlatformTransactionManager
transactionManager()
{
SessionFactory sf = sessionFactory().getObject();
org.springframework.orm.hibernate3.HibernateTransactionManager bean = new org.springframework.orm.hibernate3.HibernateTransactionManager();
bean.setSessionFactory(sf);
return bean;
}
private static Logger sLogger = Logger.getLogger(AppConfig.class);
}
/** *******************************************************************************************************************
Web context configuration.
*/
#Configuration
#EnableWebMvc
#ComponentScan(basePackageClasses = { com.mycontrollerclasses })
public
static
class
WebConfig
extends
org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
{
#Bean
public
InternalResourceViewResolver
getInternalResourceViewResolver()
{
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
return resolver;
}
#Bean
public
org.springframework.web.multipart.commons.CommonsMultipartResolver
multipartResolver()
{
return new org.springframework.web.multipart.commons.CommonsMultipartResolver();
}
#Override
public
void
addResourceHandlers(ResourceHandlerRegistry inRegistry)
{
inRegistry.addResourceHandler("/assets/**").addResourceLocations("/assets/").setCachePeriod(31556926);
inRegistry.addResourceHandler("/css/**").addResourceLocations("/css/").setCachePeriod(31556926);
inRegistry.addResourceHandler("/js/**").addResourceLocations("/js/").setCachePeriod(31556926);
}
private static Logger sLogger = Logger.getLogger(WebConfig.class);
}
/** *******************************************************************************************************************
Security configuration.
*/
#Configuration
#EnableWebMvcSecurity
public
static
class
SecurityConfig
extends
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
{
#Autowired
public
void
configureGlobal(AuthenticationManagerBuilder inAuth)
throws
Exception
{
sLogger.warn("configureGlobal =================================================");
inAuth
.inMemoryAuthentication()
.withUser("user")
.password("password")
.roles("USER");
}
private static Logger sLogger = Logger.getLogger(SecurityConfig.class);
}
public
static
class
SecurityWebApplicationInitializer
extends
org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer
{
}
}
According to https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#abstractsecuritywebapplicationinitializer-with-spring-mvc, you have to introduce a second WebApplicationInitializer, typically derivating from AbstractSecurityWebApplicationInitializer
This second WebApplicationInitializer would register property the springFilterChain
Answering using 5.0.6 Spring Security version:
#EnableWebSecurity already contains #Configuration, therefore
specifying both is redundant
You are indeed required to have a second appinitializer, which extends AbstractSecurityWebApplicationInitializer. Your one seems OK
Your getRootConfigClasses is also OK.
You really should register SecurityConfig.class there.
Your thoughts and configuration seems right, except I'm not really sure if keeping your SecurityWebApplicationInitializer as static inner class allows Spring to find it. Maybe it can be a problem. I did all the configuration acc to above mentioned instruction and everything is working like a charm, redirecting me to "/login" page.
I have created 'for now' a simple and basic spring web application. I am used to have a deployment descriptor as a simple web.xml file, and then an application context as a xml file.
Though, now i wanted to try to create my entire spring web application using only java files. Therefore i have created my WebApplicationInitializer instead of the normal deployment descriptor, and my application context which uses the #Configuration annotation.
Deployment Descriptor
package dk.chakula.config;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
/**
*
* #author martin
* #since 12-1-2012
* #version 1.0
*/
public class Initializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext)
throws ServletException {
registerDispatcherServlet(servletContext);
}
private void registerDispatcherServlet(final ServletContext servletContext) {
WebApplicationContext dispatcherContext = createContext(ChakulaWebConfigurationContext.class);
DispatcherServlet dispatcherServlet = new DispatcherServlet(dispatcherContext);
Dynamic dispatcher = servletContext.addServlet("dispatcher", dispatcherServlet);
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
private WebApplicationContext createContext(final Class<?>... annotatedClasses) {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(annotatedClasses);
return context;
}
} //End of class Initializer
Application context
package dk.chakula.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.UrlBasedViewResolver;
import org.springframework.web.servlet.view.tiles2.TilesConfigurer;
import org.springframework.web.servlet.view.tiles2.TilesView;
/**
*
* #author martin
* #since 12-01-2013
* #version 1.0
*/
#Configuration
#EnableWebMvc
#ComponentScan("dk.chakula.web")
public class ChakulaWebConfigurationContext {
#Bean
public TilesConfigurer setupTilesConfigurer() {
TilesConfigurer configurer = new TilesConfigurer();
String[] definitions = {"/layout/layout.xml"};
configurer.setDefinitions(definitions);
return configurer;
}
#Bean
public UrlBasedViewResolver setupTilesViewResolver() {
UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
viewResolver.setViewClass(TilesView.class);
return viewResolver;
}
} //End of class ChakulaWebConfigurationContext
My problem is that I can't seem to find a way 'isolate' my mapping to resources folder which contains images, css javascript etc. When my application context is in java.
With the normal XML application context I used this tag to isolate the mapping to /resources/
<mvc:resources mapping="/resources/**" location="/resources/" />
How can I do this, so my web application can use my images, css etc.
To be able to serve static resources in Spring MVC application you need two XML-tags: <mvc:resources/> and <mvc:default-servlet-handler/>. The same in the Java-based Spring configuration will be:
#Configuration
#EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
// equivalents for <mvc:resources/> tags
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/css/**").addResourceLocations("/css/").setCachePeriod(31556926);
registry.addResourceHandler("/img/**").addResourceLocations("/img/").setCachePeriod(31556926);
registry.addResourceHandler("/js/**").addResourceLocations("/js/").setCachePeriod(31556926);
}
// equivalent for <mvc:default-servlet-handler/> tag
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
// ... other stuff ...
}
Note that since #EnableWebMvc annotation is used there's no need to extend directly WebMvcConfigurationSupport, and you should just extend WebMvcConfigurerAdapter. See JavaDoc for #EnableWebMvc for details.
After using hours searching on the internet reading about Spring MVC 3 using only java files I fell over some articles which used an approach by extending from WebMvcConfigurationSupport class, and then overriding 2 methods - addResourceHandler( ResourceHandlerRegistry ) and ResourceHandlerMapping().
My new Application context now look like this.
package dk.chakula.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.servlet.view.UrlBasedViewResolver;
import org.springframework.web.servlet.view.tiles2.TilesConfigurer;
import org.springframework.web.servlet.view.tiles2.TilesView;
/**
*
* #author martin
* #since 12-01-2013
* #version 1.0
*/
#Configuration
#EnableWebMvc
#ComponentScan("dk.chakula.web")
public class ChakulaWebConfigurationContext extends WebMvcConfigurationSupport {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Override
#Bean
public HandlerMapping resourceHandlerMapping() {
AbstractHandlerMapping handlerMapping = (AbstractHandlerMapping) super.resourceHandlerMapping();
handlerMapping.setOrder(-1);
return handlerMapping;
}
#Bean
public TilesConfigurer setupTilesConfigurer() {
TilesConfigurer configurer = new TilesConfigurer();
String[] definitions = {"/layout/layout.xml"};
configurer.setDefinitions(definitions);
return configurer;
}
#Bean
public UrlBasedViewResolver setupTilesViewResolver() {
UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
viewResolver.setViewClass(TilesView.class);
return viewResolver;
}
} //End of class ChakulaWebConfigurationContext
As I understood We had to override addResourceHandler, to add the location and the mapping of resources to the registry. Thereafter we needed a bean which returned an object of HandlerMapping. The order of this HandlerMapping should be set to -1, because as I could read from the spring documentation, then -1 means
HandlerMapping ordered at Integer.MAX_VALUE-1 to serve static
resource requests.
My application can now load the css files and images into their views, and I wanted to enlighten you others with the answer so, people in the future could get benefit of this.
Try this:
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}