Spring 4 REST application using Java configuration (no xml) IllegalArgumentException [duplicate] - java

This question already has answers here:
java.lang.IllegalArgumentException: Failed to register servlet with name 'dispatcher'.Check if there is another servlet registered under the same name
(2 answers)
Closed 5 years ago.
This is driving me crazy. I've done a good bit of Spring coding but this is the first time that I'm trying to do all configuration in Java. I've searched all over stackoverflow and other places, and while others have seen this problem, none of their solutions have worked for me.
Here are my source files:
Initializer:
public class EnhancedCandidateInfoInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
private static final Logger logger = LoggerFactory.getLogger(EnhancedCandidateInfoInitializer.class);
#Override
protected Class<?>[] getRootConfigClasses() {
logger.info("##### getRootConfigClasses called - returning null #####");
return null;
}
#Override
protected Class<?>[] getServletConfigClasses() {
logger.info("##### getServletConfigClasses called returning EnhancedCandidateInfoWebConfiguration.class #####");
return new Class[] { EnhancedCandidateInfoWebConfiguration.class };
}
#Override
protected String[] getServletMappings() {
logger.info("##### getServletMappings called #####");
return new String[] { "/" };
}
}
WebMvcConfiguration:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"com.mojorank.restapi"})
public class EnhancedCandidateInfoWebConfiguration extends WebMvcConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(EnhancedCandidateInfoWebConfiguration.class);
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
logger.info("#### My Configuration handler was called ####");
configurer.enable();
}
}
Controller:
#RestController
public class EnhanceCandidateInfoController {
#RequestMapping("/")
public String welcome() {//Welcome page, non-rest
return "Welcome to RestTemplate Example.";
}
#RequestMapping("/hello/{player}")
public Message message(#PathVariable String player) {//REST Endpoint.
Message msg = new Message(player, "Hello " + player);
return msg;
}
}
When I build and deploy the application to tomcat, I get the following exception stack trace:
Caused by: java.lang.IllegalArgumentException: Failed to register servlet with name 'dispatcher'.Check if there is another servlet registered under the same name.
at org.springframework.util.Assert.notNull(Assert.java:115)
at org.springframework.web.servlet.support.AbstractDispatcherServletInitializer.registerDispatcherServlet(AbstractDispatcherServletInitializer.java:98)
at org.springframework.web.servlet.support.AbstractDispatcherServletInitializer.onStartup(AbstractDispatcherServletInitializer.java:71)
at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:169)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5274)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
As I said, I've searched through stackoverflow and other places and found others that had my same problem, but when I tried to implement the proposed fixes, my problem remained. Thanks in advance for any help.

Change your WebMvcConfiguration method to this:
public class EnhancedCandidateInfoWebConfiguration extends WebMvcConfigurerAdapter {
#Override
public void configureViewResolvers(ViewResolverRegistry registry) {
InternalResourceViewResolver ivr=new InternalResourceViewResolver();
ivr.setPrefix("/WEB-INF/jsp/");
ivr.setSuffix(".jsp");
ivr.setExposeContextBeansAsAttributes(true);
registry.viewResolver(ivr);
}
}

Related

Dependency Injection into Spring Servlet context (OncePerRequestFilter)

I implemented a OncePerRequestFilter, where in the doFilterInternal() I would like to use an utilization class, that used JdbcTemplate and user data from a properties file. I realized that it couldn't reach the data from the properties file (database connection and variables) and has null value all the time. As I found on the internet it's, because of the different context.
I could successfully setup a new jdbc datasource locally, but I wouldn't like to duplicate the code, so I would like to inject simply the sources the same way as I did everywhere else like in RestControllers (#Value, #Autowired).
Any idea, how could I inject these in my utilization class that will be used in the servlet filter or directly in my filter?
Thank you!
UPDATE - code snippets:
In the RestController, the injection of JdbcTemplate works properly, but in the filter I cannot inject it, always throws nullPointerException.
#SpringBootApplication
public class AsdApplication {
public static void main(String[] args) {
SpringApplication.run(AsdApplication.class, args);
}
public static class ApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Filter[] getServletFilters() {
DelegatingFilterProxy delegateFilterProxy = new DelegatingFilterProxy();
delegateFilterProxy.setTargetBeanName("MyFilter");
return new Filter[] { delegateFilterProxy };
}
#Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return null;
}
}
}
#RestController
public class RestCtrl {
#Autowired
private JdbcTemplate jdbcTemplate;
#GetMapping("/test")
public ResponseEntity<String> getTest() {
String result = jdbcTemplate.queryForObject("<query>", String.class);
System.out.println("result in ctrl: " + result);
return new ResponseEntity<>("asd ad asd asd asd", HttpStatus.OK);
}
}
#Component(value = "MyFilter")
public class MyFilter extends OncePerRequestFilter {
#Autowired
private JdbcTemplate jdbcTemplate;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String result = jdbcTemplate.queryForObject("<query>", String.class);
System.out.println("result in filter: " + result);
User currentUser = new User("username", "password", new ArrayList<>());
UsernamePasswordAuthenticationToken authenticatedUser = new UsernamePasswordAuthenticationToken(
currentUser, null, currentUser.getAuthorities()
);
SecurityContextHolder.getContext().setAuthentication(authenticatedUser);
filterChain.doFilter(request, response);
}
}
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable().authorizeRequests().anyRequest().authenticated();
httpSecurity.addFilterBefore(new MyFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
spring.datasource.url=jdbc:<sqlserver>
spring.datasource.username=<user>
spring.datasource.password=<pass>
spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
As you are actually using Spring Boot and want to make it part of the Spring Security filter chain (which is something different!) what you need to do is
Create an #Bean method to create the filter and make it a bean
Create an #Bean method and add a FilterRegistration bean to prevent the bean from being registered as a filter by Spring Boot
Configure Spring Security.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable().authorizeRequests().anyRequest().authenticated();
httpSecurity.addFilterBefore(myFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
#Bean
public MyFilter myFilter() {
return new MyFilter();
}
#Bean
public FilterRegistrationBean<MyFilter> myFilterRegistationBean() {
FilterRegistationBean frb = new FilterRegistrationBean(myFilter());
frb.setEnabled(false);
return frb;
}
Finally remove the #Component from your MyFilter as you don't need it and it would create an additional instance. All prior changes (like the ApplicationInitializer etc. you can remove.
NOTE: As you are using Spring Security and somehow use this for authentication, instead of extending OncePerRequestFilter I suggest you extend the Spring Security AbstractAuthenticationProcessingFilter which integrates better with Spring Security (like fireing events for authentication, logging etc.).
I see you are creating a new instance of MyFilter instead of using the one managed by Spring with #Component(value = "MyFilter")
httpSecurity.addFilterBefore(new MyFilter(), UsernamePasswordAuthenticationFilter.class);
Hence you will hit a NPE since jdbcTemplate is null. You can inject the instance managed be Spring instead of creating a new one.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
#Qualifier("MyFilter")
private MyFilter myFilter;
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable().authorizeRequests().anyRequest().authenticated();
httpSecurity.addFilterBefore(myFilter, UsernamePasswordAuthenticationFilter.class);
}
}
You should use this:
Through this class you can get different Spring Boot Beans in a non Bean class.
#Component
public class ApplicationContextUtils implements ApplicationContextAware {
private static ApplicationContext ctx;
#Override
public void setApplicationContext(ApplicationContext appContext)
throws BeansException {
ctx = appContext;
}
public static ApplicationContext getApplicationContext() {
return ctx;
}
}
Then after creating it, get your bean this way:
ApplicationContext appCtx = ApplicationContextUtils.getApplicationContext();
// Here you get your dependency
ARequiredClass dependency = appCtx.getBean(ARequiredClass.class);

Spring #CacheEvict not working

I've experience in Spring MVC, but first time using Cache. These are steps that I've done yet.
Step : 1
// In spring config
#Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("user");
}
// Cached Object
public class CachedUser {
private String username;
private String token;
// Public getter-setter
}
// AuthServiceImp
#Service
public class AuthServiceImp implements AuthService {
#Override
#Cacheable(value="user", key="#token")
#Transactional
public CachedUser loadUserDetailsFromDb(String username, String token) {
// codes here
}
#Override
#CacheEvict(value="user", key="#token")
#Transactional
public void removeUser(String username, String token) {
// codes here
}
}
// My Filter
public class AuthenticationTokenFilter extends UsernamePasswordAuthenticationFilter {
AuthService authService = WebApplicationContextUtils
.getRequiredWebApplicationContext(this.getServletContext())
.getBean(AuthService.class);
CachedUser user = this.authService.loadUserDetailsFromDb(username, authToken);
}
// Controller
#RestController
public class AuthenticationController {
#Autowired
private AuthService authService;
#GetMapping("logout2")
public ResponseModel logout(#RequestAttribute("username") String username,
HttpServletRequest request) {
String token = request.getHeader(tokenHeader);
authService.removeUser(username, token);
return new ResponseModel(200,"Success",null);
}
}
Whenever calling loadUserDetailsFromDb from AuthenticationTokenFilter it returns cached object (except in first call obviously). That means #Cacheable(value="user", key="#token") is working fine.
But even after I logged out and called authService.removeUser(), calling loadUserDetailsFromDb() fetches the cached object. That means #CacheEvict(value="user", key="#token") is not working.
Step: 2
Referred this and moved removeUser() to another service ( say CacheServiceImp implements CacheService ), yet same problem.
Step: 3
Reffered this and , by my understanding, moved #Cache* annotation to interface AuthService, got following error.
java.lang.IllegalArgumentException: Null key returned for cache
operation (maybe you are using named params on classes without debug
info?)
Note : Is the problem of not evicting, because I'm calling #Cacheable and #CacheEvict methods from different classes. That is from AuthenticationTokenFilter and AuthenticationController
After playing with my code, head and internet, at last, I got this solved. It's a mistake in my Spring (Security) configuration, which I failed to post with the question.
Mistake 1 :
In SecurityInitializer class
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
public SecurityInitializer() {
super(WebSecurityConfiguration.class);
}
}
As the project includes Spring MVC configuration, the constructor must not be implemented. So removed the constructor. This class, then, simply registers the springSecurityFilterChain Filter for every URL.
Mistake 2: ( THE REAL CAUSE OF ABOVE PROBLEM )
I've added my AuthenticationTokenFilter in two ways:
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
// other overrides
#Override
protected Filter[] getServletFilters() {
return new Filter[]{ new AuthenticationTokenFilter() };
}
}
and
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
// Other config
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
//Other config
httpSecurity.addFilterBefore(authTokenFilter,
UsernamePasswordAuthenticationFilter.class);
}
}
This made the filter to be called twice, one inside Spring context and the other as usual Servlet filter
So removed configuration inside WebAppInitializer
Additional change
Removed #ComponentScan from WebSecurityConfiguration because it's already in SpringMvcConfig. This requires both configurations to be loaded in same context. Done by following code.
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { SpringMvcConfig.class, WebSecurityConfiguration.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
// Removed filter registering from here (Mistake 2)
}
At last, everything working FINE :)

Troubles running spring mvc java based configuration in idea local tomcat

I created to simple spring mvc configuration using java based configuration:
Config file:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.kitchen")
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/kitchen/**").addResourceLocations("/kitchen/");
registry.addResourceHandler("/images/**").addResourceLocations("file:E:/Work/images/");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
Initializer:
public class WebMvcAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[0];
}
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{WebMvcConfiguration.class};
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
#Override
protected Filter[] getServletFilters() {
return new Filter[]{new CORSFilter()};
}
}
Controller:
#Controller
public class IndexController {
#RequestMapping(value = "/")
public String getIndexPage() {
return "kitchen/index.html";
}
}
All files are located in same package. But when I try to deploy in tomcat, nothing is deployed. I am not good in configurations so I would like to ask maybe I forgot something more? I do not want to use web.xml, just plain java configuration.
Also there could be problem creating modules and artifacts in in Idea IDE I moved not a lot of time ogo to it from eclipse. so here is everithing a little bit different. Here are my configurations of project modules and artifacts, can you please tell me what could be problems in my situation?
Screens:

Spring 3.2 property field populated using #value is found null during method call

I have following classes:
#Component
public class TestParentClass{
#Value("${service.timeout}")
protected String timeout;
#PostConstruct
protected void init() {
//some code
}
protected Boolean someMethod(Agr agr0, Agr agr2){
//some code
}
}
#Component
#Primary
public class TestSubClass extends TestParentClass{
#PostConstruct
#Override
protected void init() {
System.out.println("Test0:" + timeout);
super.init();
}
#Override
protected Boolean someMethod(Agr agr0, Agr agr2){
System.out.println("Test1:" + timeout);
return super.someMethod(agr0,agr1);
}
}
Properties file has entry as
service.timeout=30
and is included in import resources in spring configuration.
When I run the application having these classes, during deployment log shows
Test0:30
but when the someMethod called log shows
Test1:null
Any help in resolving this issue is appreciated, thanks in advance!

Tomcat 7 and AbstractSecurityWebApplicationInitializer are not working together

I was following this article to try a basic http-auth without the use of a web.xml
I'm using Tomcat 7.0.41 and those are my dependencies on gradle:
ext.springVersion = "3.2.1.RELEASE"
compile "org.springframework:spring-jdbc:$springVersion",
"org.springframework:spring-context:$springVersion",
"org.springframework:spring-web:$springVersion",
"org.springframework:spring-webmvc:$springVersion",
"org.springframework.security:spring-security-core:3.2.0.M2",
"org.springframework.security:spring-security-web:3.2.0.M2",
"org.springframework.security:spring-security-config:3.2.0.M2",
According to the tutorial I defined the following
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("admin")
.roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeUrls().antMatchers("/").hasRole("USER")
.and().httpBasic();
}
}
then added that class to the initializer like this:
#Order(1)
public class ServletConfiguration extends
AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { SecurityConfiguration.class };
// return null;
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { AppConfiguration.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
// #Override
// protected Dynamic registerServletFilter(ServletContext servletContext,
// Filter filter) {
// Dynamic securityFilter = servletContext.addFilter(
// "springSecurityFilterChain", DelegatingFilterProxy.class);
// securityFilter.addMappingForUrlPatterns(
// EnumSet.allOf(DispatcherType.class), false, "/*");
// return securityFilter;
// }
}
and finally added a class to inizialize the springSecurityFilterChain:
#Order(2)
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
#Override
protected void afterSpringSecurityFilterChain(ServletContext servletContext) {
System.out.println("afterSpringSecurityFilterChain");
super.afterSpringSecurityFilterChain(servletContext);
}
}
But I'm always getting this error:
DEBUG: org.springframework.jndi.JndiPropertySource - JNDI lookup for name [spring.liveBeansView.mbeanDomain] threw NamingException with message: Name [spring.liveBeansView.mbeanDomain] is not bound in this Context. Unable to find [spring.liveBeansView.mbeanDomain].. Returning null.
Jul 11, 2013 9:22:24 PM org.apache.catalina.core.StandardContext filterStart
SEVERE: Exception starting filter springSecurityFilterChain
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'springSecurityFilterChain' is defined
I don't why though, because when I go debugging the server initialization, actually these two methods are called:
#Override
public final void onStartup(ServletContext servletContext)
throws ServletException {
if(enableHttpSessionEventPublisher()) {
servletContext.addListener(HttpSessionEventPublisher.class);
}
insertSpringSecurityFilterChain(servletContext);
afterSpringSecurityFilterChain(servletContext);
}
then
private void insertSpringSecurityFilterChain(ServletContext servletContext) {
String filterName = "springSecurityFilterChain";
DelegatingFilterProxy springSecurityFilterChain = new DelegatingFilterProxy(filterName);
String contextAttribute = getWebApplicationContextAttribute();
if(contextAttribute != null) {
springSecurityFilterChain.setContextAttribute(contextAttribute);
}
registerFilter(servletContext, true, filterName, springSecurityFilterChain);
}
So actually the filter gets created. But then it gets lost somewhere.
I tried to play with #Order, but that was doing nothing so I tried to register springSecurityFilterChain using the registerServletFilter method but I'm not getting any http-auth request authentication.
And also SecurityConfiguration doens't even get loaded.
SecurityInitializer creates the DelegatingFilterProxy which is used to look up a bean by the name of springSecurityFilterChain. The springSecurityFilterChain is created using #EnableWebSecurity. The problem is that you are missing the #Configuration annotation (without it the Root ApplicationContext is not even going to try to load the SecurityConfiguration). Specifically you want to do the following:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
...
}
A few additional things to point out:
You do not need to use the #Order because you are not adding any other Filters
The only URL you have secured is the context root (i.e. /).
You will want to be aware of a bug with httpBasic() that is discussed on need spring security java config example showing basic auth only
UPDATE: I should have also pointed out that I have already logged SPR-10660 to support #Enable* annotations without having #Configuration on them. After that is resolved, your issue would magically go away.

Categories