Spring MVC Java Config issues - spring-mvc

I am trying to do an example to change the xml config to Java config for Spring MVC. But my simple example is not working. On running this project on server, I can't see any beans initializing or the dispatcher servlet name on console.
and I get 404 error on running the http://localhost:8080/Servlet3Example/
I have created a maven project and following is my code:
package com.project.config;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class MyDispatcherServlet extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
System.out.println("get root config");
//return new Class[]{RootConfig.class};
return null;
}
#Override
protected Class<?>[] getServletConfigClasses() {
System.out.println("get web config");
return new Class[]{WebConfig.class};
}
#Override
protected String[] getServletMappings() {
System.out.println("in dispatcher servlet");
return new String[] {"/"};
}
}
And the WebConfig is:
package com.project.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Configuration
#EnableWebMvc
#ComponentScan(basePackages={"com.project.controllers"})
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setSuffix(".jsp");
viewResolver.setPrefix("/WEB-INF/views/");
return viewResolver;
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
And controller:
package com.project.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
public class HomeController {
#RequestMapping(value="/",method=RequestMethod.GET)
public String home()
{
return "home";
}
}

Can you please change #ComponentScan to #ComponentScan(basePackages={"com.project.*"})

Related

How to auto login after successful registration in spring boot?

Yes, I know this question is already asked. But regard to this question I have a different problem as well. authenticationManager object of AuthenticationManager in SecurityServiceImpl class which is not authenticating the details which caused me to stuck at this point.
I have tried to give message after each step by which I can track the particular code whether they are working or not and I found in SecurityServiceImpl class that it does not work after this code
authenticationManager.authenticate(usernamePasswordAuthenticationToken);
I don't know this is just my understanding. Help me out of this problem and please provide me if you have better code snippet to solve this particular problem.
SecurityConfiguration
package com.demo.practice.configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);
#Autowired
private UserDetailsService userDetailsService;
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/","/register","/login").permitAll()
.antMatchers("/student/**").hasAuthority("STUDENT")
.antMatchers("/admin/**").hasAuthority("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin().loginPage("/login")
.defaultSuccessUrl("/dashboard");
http.csrf().disable();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
UserServiceImpl
package com.demo.practice.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import com.demo.practice.model.Credentials;
import com.demo.practice.model.Role;
import com.demo.practice.model.User;
import com.demo.practice.repository.UserRepository;
#Service
public class UserServiceImpl implements UserServiceInterface {
#Autowired
private BCryptPasswordEncoder encoder;
#Autowired
UserRepository userRepo;
#Override
public void saveUser(User user,Credentials credential) {
user.setCredential(credential);
user.getCredential().setUsername(user.getEmail());
user.getCredential().setRoles(Role.STUDENT);
user.getCredential().setPassword(encoder.encode(user.getCredential().getPassword()));
userRepo.save(user);
}
#Override
public User findUserByEmail(String name) {
User user=userRepo.findUserByEmail(name);
return user;
}
}
UserDetailsServiceImpl
package com.demo.practice.service;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.demo.practice.model.Credentials;
import com.demo.practice.repository.CredentialRepository;
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
CredentialRepository credRepo;
#Override
#Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Credentials credential =credRepo.findByUsername(username);
List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
grantedAuthorities.add(new SimpleGrantedAuthority(credential.getRoles().toString()));
return new org.springframework.security.core.userdetails.User(credential.getUsername(), credential.getPassword(), grantedAuthorities);
}
}
SecurityServiceImpl
package com.demo.practice.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
#Service
public class SecurityServiceImpl implements SecurityServiceInterface {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private UserDetailsService userDetailsService;
private static final Logger logger = LoggerFactory.getLogger(SecurityServiceImpl.class);
#Override
public String findLoggedInUsername() {
Object userDetails = SecurityContextHolder.getContext().getAuthentication().getDetails();
if (userDetails instanceof UserDetails) {
return ((UserDetails)userDetails).getUsername();
}
return null;
}
#Override
public void autologin(String username, String password) {
System.out.println("in autologin "+username);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
System.out.println("in autologin at userdetails"+userDetails);
logger.info("after userdetails "+userDetails);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(),userDetails.getAuthorities());
logger.info("in autologin after usernamepasswordauthentication! ", usernamePasswordAuthenticationToken);
authenticationManager.authenticate(usernamePasswordAuthenticationToken);
logger.info("after authentication manager ", usernamePasswordAuthenticationToken);
if (usernamePasswordAuthenticationToken.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
logger.debug(String.format("Auto login %s successfully!", username));
}
else System.out.println("auto login failed");
}
}
you are missing one thing, if you want to have an authentication session based, you need to add WebAuthenticationDetails to the token like:
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(principalUser, null, List.of(new SimpleGrantedAuthority(principalUser.getRole())));
token.setDetails(new WebAuthenticationDetails(request));
SecurityContextHolder.getContext().setAuthentication(token);
the WebAuthenticationDetails from the doc:
Records the remote address and will also set the session Id if a session already exists (it won't create one).
for more info about the login/logout process take a look at:
https://github.com/pezetem/spring-security-angular-skeleton/blob/master/src/main/java/com/pezetem/blog/code/spring_security_custom_authorizers/security/SecurityController.java
UPDATE:
ok, there could be another reason why it is not working for you. Spring Security adds default prefix to the roles. It is equal to ROLE_. It means that when you have an endpoint configured for .hasAuthority("STUDENT") role must be equal to ROLE_STUDENT, take a look at the line where you create a user, you assign role with Role.STUDENT and the enum value should be ROLE_STUDENT not STUDENT

How to resolve view in thymleaf + Springboot?

Currently, i have function, which is to convert the data from MYSQL to CSV. The CSV function contain the webconfig where use the viewResolver. The problem is, when i used below function, the page cannot view but the CSV file can be download and vice versa. Is there anything that i need to configure ?
-Configure ContentNegotiatingViewResolver
#Bean
public ViewResolver contentNegotiatingViewResolver(ContentNegotiationManager manager) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(manager);
// Define all possible view resolvers
List<ViewResolver> resolvers = new ArrayList<>();
resolvers.add(csvViewResolver());
resolver.setViewResolvers(resolvers);
return resolver;
}
WebConfig- full code
package com.portal.dmtt.csvDownload.config;
import com.portal.dmtt.csvDownload.viewResolver.CsvViewResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
import java.util.ArrayList;
import java.util.List;
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer
.defaultContentType(MediaType.APPLICATION_JSON)
.favorPathExtension(true);
}
/*
* Configure ContentNegotiatingViewResolver
*/
#Bean
public ViewResolver contentNegotiatingViewResolver(ContentNegotiationManager manager) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
resolver.setContentNegotiationManager(manager);
// Define all possible view resolvers
List<ViewResolver> resolvers = new ArrayList<>();
resolvers.add(csvViewResolver());
resolver.setViewResolvers(resolvers);
return resolver;
}
/*
* Configure View resolver to provide Csv output using Super Csv library to
* generate Csv output for an object content
*/
#Bean
public ViewResolver csvViewResolver() {
return new CsvViewResolver();
}
}
Export Controller
package com.portal.dmtt.csvDownload.controller;
import com.portal.dmtt.repo.dmttDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
#Controller
public class ExportController {
#Autowired
private dmttDAO dmttDAO;
/**
* Handle request to download an Excel document
*/
#GetMapping("/dl")
public String download(Model model) {
model.addAttribute("results", dmttDAO.getAllResultSet());
return "";
}
}
Abstract View
package com.portal.dmtt.csvDownload.view;
import org.springframework.web.servlet.view.AbstractView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
public abstract class AbstractCsvView extends AbstractView {
private static final String CONTENT_TYPE = "text/csv";
public AbstractCsvView() {
setContentType(CONTENT_TYPE);
}
#Override
protected boolean generatesDownloadContent() {
return true;
}
#Override
protected final void renderMergedOutputModel(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
response.setContentType(getContentType());
buildCsvDocument(model, request, response);
}
protected abstract void buildCsvDocument(
Map<String, Object> model, HttpServletRequest request, HttpServletResponse response)
throws Exception;
}
CSV View
package com.portal.dmtt.csvDownload.view;
import com.portal.dmtt.model.exceptionMonitoring.FN_Result_Set;
import org.supercsv.io.CsvBeanWriter;
import org.supercsv.io.ICsvBeanWriter;
import org.supercsv.prefs.CsvPreference;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
public class CsvView extends AbstractCsvView {
#Override
protected void buildCsvDocument(Map<String, Object> model, HttpServletRequest request, HttpServletResponse
response) throws Exception {
response.setHeader("Content-Disposition", "attachment; filename=\"my-csv-file.csv\"");
List<FN_Result_Set> fnResultSetList = (List<FN_Result_Set>) model.get("results");
String[] header = {"SP_ID", "SP_ID", "XFER_XMIT_STATUS", "XFER_FILE_NAME", "UPDATE_TS", "YYMM", "REMARKS"};
try {
ICsvBeanWriter csvWriter = new CsvBeanWriter(response.getWriter(),
CsvPreference.STANDARD_PREFERENCE);
csvWriter.writeHeader(header);
for (FN_Result_Set user : fnResultSetList) {
csvWriter.write(user, header);
}
csvWriter.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
View Resolver
package com.portal.dmtt.csvDownload.viewResolver;
import com.portal.dmtt.csvDownload.view.CsvView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import java.util.Locale;
public class CsvViewResolver implements ViewResolver {
#Override
public View resolveViewName(String s, Locale locale) throws Exception {
return new CsvView();
}
}
One of the problems is that your CSVViewResolver is resolving a view for any view name. You may want to return null from CSVViewResolver.resolveViewName() if s, the view name, is not empty .
Another issue is that the browser (at least my Chrome) doesn't send text/csv as Accept header, but text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Set the media type explicitly in configureContentNegotiation for CSV:
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer
.defaultContentType(MediaType.APPLICATION_JSON)
.favorPathExtension(true)
.mediaType("csv", MediaType.parseMediaType("text/csv"));
}
Remove the Bean contentNegotiatingViewResolver
You shouldn't create a contentNegotiatingViewResolver as one is provided by Spring Boot. If you provide one, you will have two of this type, and yours will not have the Thymeleaf ViewResolver. As your CSVViewResolver will return a view for any view name, the view will be resolved in the ContentNegotiatingViewResolver provided by you, not in the one provided by Spring.
Details:
The csvViewResolver bean will be picked up by the Spring Boot's ContentNegotiatingViewResolver along others like BeanNameViewResolver, ThymeleafViewResolver, ViewResolverComposite, InternalResourceViewResolver.
To debug this set a breakpoint on DispatcherServlet.resolveViewName:
protected View resolveViewName(String viewName, Map<String, Object> model, Locale locale,
HttpServletRequest request) throws Exception {
for (ViewResolver viewResolver : this.viewResolvers) {
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}

How to test spring 5 controllers with Junit5

I'm trying to test my Spring 5 web controllers with JUnit 5.
The two way to test controller (as mentionned in spring documentation) always give me null pointer.
This is my test class
import com.lacunasaurus.gamesexplorer.test.configuration.TestBackEndConfiguration;
import com.lacunasaurus.gamesexplorer.test.configuration.TestWebConfig;
import org.junit.Before;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
#RunWith(JUnitPlatform.class)
#ExtendWith(SpringExtension.class)
#WebAppConfiguration()
#ContextConfiguration(classes = {TestWebConfig.class, TestBackEndConfiguration.class})
public class TestAuthenticationCreateAccountController {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup() {
// this.mockMvc = MockMvcBuilders.standaloneSetup(new AuthenticationCreateAccountController()).build();
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
public void getAccount() throws Exception {
// Here i've got an null pointer
mockMvc.perform(get("/"));
}
}
Here my web configuration for tests
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
#ComponentScan(basePackages = {"com.lacunasaurus.gamesexplorer.web"})
public class TestWebConfig implements WebMvcConfigurer, ApplicationContextAware {
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
And now my controller
#Controller
#RequestMapping("/create-account")
public class AuthenticationCreateAccountController {
#Autowired
UserAccountValidator accountValidator;
#Autowired
AuthenticationService authenticationService;
#GetMapping
public String navigate() {
return "authentication/create-account";
}
#ModelAttribute("userAccount")
public UserAccount setDefaultAccount() {
return new UserAccount();
}
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(accountValidator);
}
#PostMapping
public String createAccount(#Validated UserAccount userAccount, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "authentication/create-account";
}
authenticationService.createUserAccount(userAccount);
return "authentication/create-account";
}
}
EDIT : The stacktrace given by the IDE
java.lang.NullPointerException at com.lacunasaurus.gamesexplorer.test.controller.TestAuthenticationCreateAccountController.getAccount(TestAuthenticationCreateAccountController.java:41)
Results :
Tests in error:
TestAuthenticationCreateAccountController.getAccount:41 NullPointer
I've got already test my backend with junit 5 and spring and everything work well.
Thanks to thoses who will help me to understand how to test controller :)
The new test for controllers :
#ExtendWith(SpringExtension.class)
#WebAppConfiguration()
#ContextConfiguration(classes = {TestWebConfig.class, TestBackEndConfiguration.class})
#TestInstance(Lifecycle.PER_CLASS)
public class TestAuthenticationCreateAccountController {
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#BeforeEach
void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
#Test
void getAccount() throws Exception {
mockMvc.perform(get("/toto")).andExpect(status().isOk());
}
}
If you're using Junit5, See this reference
#ExtendWith(MockitoExtension.class) // For Junit5
public class TestAuthenticationCreateAccountController {
#Mock
private WebApplicationContext wac;
#InjectMocks
private AuthenticationCreateAccountController ac;
private MockMvc mockMvc;
#BeforeEach // For Junit5
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(ac).build();
}
#Test
public void getAccount() throws Exception {
mockMvc.perform(get("/"));
}
}
In order to use JUnit Jupiter, you must use autoconfigure MockMvc. This is a help to resolve all related configuration.
#ExtendWith(SpringExtension.class)
#WebAppConfiguration()
#AutoConfigureMockMvc
#Autowired
private MockMvc mockMvc;
This should fix your issue.

Configuring Multiple View Resolvers (Thymeleaf and Json) Spring

I am trying to configure a Spring based app, where I want to configure two view resolvers. From my controller, if I return just the string name like "login", then it should be handled by the Thymeleaf resolver, whereas if the controller's method returns an object, then appropriate json view should be used. When I try to run my application as configured below, I get the following error
"Could not resolve view with name 'login' in servlet with name
'dispatcher'"
Requesting you guys to look at the Java classes below. The first is the configuration class, the second is the Controller I am trying to use.
package com.gojha.web;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import org.thymeleaf.templateresolver.TemplateResolver;
#Configuration
#EnableWebMvc
#ComponentScan("com.gojha.web")
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Bean
public ViewResolver viewResolver(ContentNegotiationManager cnm) {
ContentNegotiatingViewResolver cnvr = new ContentNegotiatingViewResolver();
cnvr.setContentNegotiationManager(cnm);
return cnvr;
}
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_JSON);
}
#Bean
public TemplateResolver templateResolver() {
TemplateResolver templateResolver = new ServletContextTemplateResolver();
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
return templateResolver;
}
#Bean
public TemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
return templateEngine;
}
#Bean
public ViewResolver viewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine((SpringTemplateEngine) templateEngine());
return viewResolver;
}
}
Controller
package com.gojha.web;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import static org.springframework.web.bind.annotation.RequestMethod.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
#Controller
#RequestMapping("/")
public class LoginController {
private RestTemplate restTemplate;
private class Test {
private String a;
public Test() {
super();
}
public Test(String a) {
super();
this.a = a;
}
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
}
#Autowired
public LoginController(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
#RequestMapping(method=GET)
public String testing(){
return "login";
}
#RequestMapping(method=GET, produces="application/json")
public Test testing2(){
return new Test("wow");
}
}
I hope the code is self-explanatory.
I got it working by changing the configuration file and allocating orders to view resolvers. From what I understand, it looks like first it tries to resolve the view using ContentNegotiation, and if it fails, falls back to Thymeleaf resolver. I am marking this as the answer, if someone has a better approach, or a suggested correction, let me know.
package com.gojha.web;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import org.thymeleaf.templateresolver.ServletContextTemplateResolver;
import org.thymeleaf.templateresolver.TemplateResolver;
#Configuration
#EnableWebMvc
#ComponentScan("com.gojha.web")
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_JSON);
}
#Bean
public ViewResolver viewResolver() {
TemplateResolver templateResolver = new ServletContextTemplateResolver();
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine);
viewResolver.setOrder(2);
return viewResolver;
}
#Bean
public ViewResolver cnViewResolver(ContentNegotiationManager cnm) {
ContentNegotiatingViewResolver cnvr = new ContentNegotiatingViewResolver();
cnvr.setContentNegotiationManager(cnm);
cnvr.setOrder(1);
List<View> views = new ArrayList<View>();
views.add(jsonView());
cnvr.setDefaultViews(views);
return cnvr;
}
#Bean
public View jsonView() {
MappingJackson2JsonView view = new MappingJackson2JsonView();
view.setPrettyPrint(true);
return view;
}
}

WebApplicationContextUtils.getWebApplicationContext(ServletContext) returns NULL from ServletContextListener

I am unable to get WebApplicationContext inside ServletContextListener. I searched for solution but non of them are working for me. I would appreciate if any one can help. Thanks in advance.
Below are code snippets-
My AppConfig class:
package in.andonsystem.config;
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.ResourceBundleMessageSource;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"in.andonsystem"})
public class AppConfig extends WebMvcConfigurerAdapter{
#Bean
public ViewResolver viewResolver(){
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
#Bean
public MessageSource messageSource(){
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
}
}
My AppInitializer Class.
package in.andonsystem.config;
import javax.servlet.ServletRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
public class Appinitializer implements WebApplicationInitializer{
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(AppConfig.class);
rootContext.setServletContext(servletContext);
// Manage the lifecycle of the root application context
servletContext.addListener(new ContextLoaderListener(rootContext));
rootContext.refresh();
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher =
servletContext.addServlet("dispatcher", new DispatcherServlet(rootContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
My ContextListener Class:
package in.andonsystem.config;
import com.mysql.jdbc.AbandonedConnectionCleanupThread;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.logging.Logger;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
#WebListener
public class ContextListener implements ServletContextListener{
private Logger logger = Logger.getLogger("ContextListener");
#Override
public void contextInitialized(ServletContextEvent sce) {
logger.info("contextInitialized()");
WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
System.out.println("Is rootContext null:" + (rootContext == null));
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
stdout output is always coming true.
What am I missing?
Finally came up with the solution. The problem was that ServletContextListener was being loaded before ContextLoaderListener and Therefore a WebApplicationContext returned was null because ContextLoaderListener must be loaded before ServletContextListener in order to get the rootContext.
I removed #WebListener annotation from my ContextListener and added this Listener Programatically in onStartup method of AppInitializer after Adding ContextLoaderListener.
Now my ContextListener without #WebListener annotation is:
public class ContextListener implements ServletContextListener{
private Logger logger = Logger.getLogger("ContextListener");
#Override
public void contextInitialized(ServletContextEvent sce) {
logger.info("contextInitialized()");
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(sce.getServletContext());
System.out.println("Is rootContext null:" + (rootContext == null));
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
logger.info("contextDestroyed()");
}
}
and AppInitializer is :
public class Appinitializer implements WebApplicationInitializer{
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(AppConfig.class);
rootContext.setServletContext(servletContext);
// Manage the lifecycle of the root application context
servletContext.addListener(new ContextLoaderListener(rootContext));
//ContextListener must be added after ContextLoaderListener
servletContext.addListener(ContextListener.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher =
servletContext.addServlet("dispatcher", new DispatcherServlet(rootContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
Looks basically correct, but I think you should remove the following 2 lines from your initializer class:
rootContext.setServletContext(servletContext);
and
rootContext.refresh();

Resources