How to enforce Tomcat to accept DispatcherServlet url mapping, configured programmaticaly? - java

I'm running a hello-world Spring WebMVC sample of 3 classes: WebApplicationInitializer, WebMvcConfigurer and simple controller with a single request handler method.
Inside WebApplicationInitializer 'onStartup(ctxt)' the DispatcherServlet instance is mapped to '/app/*' URL pattern and reflected in ctxt.context.context.servletMapping value as {*.jspx=jsp, /app/*=dispatcher, *.jsp=jsp, /=default} in runtime.
But for unknown reason GET e.g. 'http://localhost:8080/app/sht' is dispatched to Tomcat's DefaultServlet! In runtime the request's context servlet mapping(request.request.applicationMapping.mappingData.context.servletMapping) value states that onStartup() configuration is lost: {*.jspx=jsp, *.jsp=jsp, /=default}
Could you please assist in configuring Spring properly? Thanks!
Spring 5.2.2; Tomcat 9.0.30, running under Eclipse.
public class WebbAppInitializer implements WebApplicationInitializer{
#Override
public void onStartup(ServletContext context) throws ServletException{
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();
dispatcherContext.setServletContext(context);
dispatcherContext.register(SpringWebConfig.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher = context.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/app/*");
}
}
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = { "cool.videoservice.controller" })
public class SpringWebConfig implements WebMvcConfigurer{
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setViewClass(JstlView.class);
bean.setPrefix("/WEB-INF/view/");
bean.setSuffix(".jsp");
return bean;
}
}
#Controller
public class IndexController{
#GetMapping("/sht")
public String greeting(#RequestParam(name="name", required=false, defaultValue="World") String name, Model model) {
model.addAttribute("name", name);
System.out.println("Oh, yeah!!!!!!");
return "index";
}
}

Related

Spring - addResourceHandlers not resolving the static resources with Rest Controller

I have the following problem. When configuring my Spring WebMVC application with ResourceHandlers and having a RestController class with a special #RequestMapping, the static resource will not be served, instead the RestController is being called.
Here is the code:
#Configuration
#ServletComponentScan
#EnableWebMvc
public class Main extends SpringBootServletInitializer implements ServletContextAware, WebMvcConfigurer {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(new Class[]{Main.class, Initializer.class, ContainerInitializer.class});
}
#Bean
public ServletRegistrationBean servletRegistrationBean() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new FacesServlet(), "*.jsf");
servletRegistrationBean.setName("JSF Faces Servlet");
servletRegistrationBean.setLoadOnStartup(1);
return servletRegistrationBean;
}
#Bean
public ServletRegistrationBean facesServletRegistratiton() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new FacesServlet(), new String[]{"*.xhtml"});
servletRegistrationBean.setName("XHTML Faces Servlet");
servletRegistrationBean.setLoadOnStartup(1);
return servletRegistrationBean;
}
#Override
public void setServletContext(ServletContext servletContext) {
servletContext.setInitParameter("com.sun.faces.forceLoadConfiguration", Boolean.TRUE.toString());
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// Register resource handler for CSS and JS
registry.addResourceHandler("/resources/**").addResourceLocations("/WEB-INF/resources/")
.setCacheControl(CacheControl.maxAge(2, TimeUnit.HOURS).cachePublic())
.resourceChain(true)
.addResolver(new PathResourceResolver());
// Register resource handler for images
registry.addResourceHandler("/images/**").addResourceLocations("/WEB-INF/images/")
.setCacheControl(CacheControl.maxAge(2, TimeUnit.HOURS).cachePublic())
.resourceChain(true)
.addResolver(new PathResourceResolver());
}
}
So I have two resource handlers for serving static resources like css, js files in the resources folder and an images folder for my static images.
The code for my RestController class looks as follows:
#RestController
#Component
public class Clownfish {
#GetMapping(path = "/{name}/**")
public void universalGet(#PathVariable("name") String name, #Context HttpServletRequest request, #Context HttpServletResponse response) {
String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
if (name.compareToIgnoreCase(path) != 0) {
name = path.substring(1);
}
}
}
When calling my resource with http://localhost:8080/images/foo.png the RestController is being called.
If I change #GetMapping(path = "/{name}/**") to #GetMapping(path = "/{name}") it works correctly. But I need the wildcards to handle special calls.
What am I doing wrong in my configuration?
TIA
I found a way to set the priority of the ResourceHandler.
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// Register resource handler for CSS and JS
registry.addResourceHandler("/resources/**").addResourceLocations("/WEB-INF/resources/")
.setCacheControl(CacheControl.maxAge(2, TimeUnit.HOURS).cachePublic())
.resourceChain(true)
.addResolver(new PathResourceResolver());
// Register resource handler for images
registry.addResourceHandler("/images/**").addResourceLocations("/WEB-INF/images/")
.setCacheControl(CacheControl.maxAge(2, TimeUnit.HOURS).cachePublic())
.resourceChain(true)
.addResolver(new PathResourceResolver());
registry.setOrder(-1); // This will set the priority lower to the default handler (that is by default 0)
}

Spring MVC application deploying multiple times in tomcat

I have application with spring mvc annotation configuration. In my local environment(IDE) it is working perfectly. When i deployed in prod(tomcat 8.5) my application listener calling multiple times. Because of this my executor tasks, schedulers calling multiple times.
AppWebAppInitializer.java
package com.app.config;
public class AppWebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(AppMvcConfig.class);
servletContext.addListener(new ContextLoaderListener(ctx));
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("appServlet",new DispatcherServlet(ctx));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/*");
dispatcher.setAsyncSupported(true);
servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE)); //uses cookies instead of jsessionId in the url.
}
}
AppMvcConfig.java
package com.app.config;
#Configuration
#EnableWebMvc
#EnableScheduling
#EnableCaching
#ComponentScan("com.app")
#Import({
AppPropertyInitConfig.class,
AppSecurityConfig.class,
WebSocketConfig.class
})
public class AppMvcConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware{
private ApplicationContext applicationContext;
#Value("${app.upload.dir}")
private String uploadDir;
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
ApplicationStartupListener.java
package com.app.listener;
#Component
public class ApplicationStartupListener {
private final Logger logger = LoggerFactory.getLogger(ApplicationStartupListener.class);
#Autowired
private LookupHolder lookupHolder;
#EventListener
public void handleContextRefreshEvent(ContextRefreshedEvent event) {
logger.info("ApplicationStartupListener initializing lookups");
Runnable lookupInitRunnable = () -> lookupHolder.init(); //initialize the lookups
new Thread(lookupInitRunnable).start();
}
}
In above code ApplicationStartupListener.java --> handleContextRefreshEvent() calling multiple times. As well as my spring default schedules also calling multiple times
In my application i don't have web.xml

Restful App with Spring MVC - Ambiguous handler methods error

I'm trying to build a Restful webapp using Spring MVC 4.
But I'm got this error "Ambiguous handler methods mapped for HTTP".
In my controller I have this endpoints:
GET /people/
GET /people/{id}
POST /people/
DELETE /peope/{id}
PUT /people/{id}
I think this error occurs because I have something wrong in my configuration.
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = { "com.myapp.rest.controller" })
public class MVCConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/site/**").addResourceLocations("/WEB-INF/site/**");
}
}
And this
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext)
throws ServletException {
WebApplicationContext context = getContext();
servletContext.addListener(new ContextLoaderListener(context));
ServletRegistration.Dynamic dispatcher = servletContext.addServlet(
"DispatcherServlet", new DispatcherServlet(context));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/*");
}
private AnnotationConfigWebApplicationContext getContext() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setConfigLocation("com.myapp.config");
return context;
}
}
Thank you all

Why doesnt my Spring Security authentication work?

I have the following configuration placed in /src/main/java/com/dog/bootstrap:
#EnableWebSecurity
#Configuration
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
System.out.println("hello");
auth.inMemoryAuthentication()
.withUser("user")
.password("password")
.roles("USER");
}
}
and I am loading it as follows:
public class WebInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) {
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();
dispatcherContext.scan("com.dog.bootstrap");
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(dispatcherContext));
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher =
container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
Set<String> mappingConflicts = dispatcher.addMapping("/");
if (!mappingConflicts.isEmpty()) {
throw new IllegalStateException("'dispatcher' could not be mapped to '/' due " +
"to an existing mapping.");
}
}
My controller:
#Controller
public class DogController {
#RequestMapping(value = {"/dog"}, method = RequestMethod.GET)
#ResponseBody
public String getSource(#PathVariable("domain") String domain) throws Exception {
return "dogs";
}
}
When I startup my app, I do see hello being printed, so configure(AuthenticationManagerBuilder auth) is being called. However, none of my endpoints are requiring me to enter a login page. When I go to localhost:8080/dog it outputs dogs without asking me to authenticate myself.
You're not actually including the filter chain, as described in the last step of this guide. Try adding this default initializer, which maps to /*:
#Component public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
}

Why doesn't component scan pick up my #Configuration?

I have a config file like
package com.mypackage.referencedata.config;
#Configuration
#ComponentScan ("com.mypackage.referencedata.*")
public class ReferenceDataConfig {
In a spring xml if I have
<context:component-scan base-package="com.mypackage.referencedata.config.*" />
it does not get loaded.
If I use
<context:component-scan base-package="com.mypackage.referencedata.*" />
it works.
What gives? I'd expect the 1st to work as well.
<context:component-scan base-package="com.mypackage.referencedata.config.*" />
Will scan packages inside com.mypackage.referencedata.config as it is package.
com.mypackage.referencedata.config
Will work fine.
You don't need to scan the #Configuration class in component scan in SpringFramework. But you need to register it in the Application Initializer class of your web application that defines the configuration required as in web.xml file. You need to implement WebApplicationInitializer interface there and define onStartup method.
In that onStartup method you need to register your #Configuration class to the rootContext of your web application. Please take a look at the following code snippet.
1. The class that works as web.xml
public class ApplicationInitializer implements WebApplicationInitializer {
//Called first when the application starts loading.
public void onStartup(ServletContext servletContext)
throws ServletException {
System.out.println("Inside application initializer...");
//Registering the class that incorporates the annotated DispatcherServlet configuration of spring
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(DispatcherConfig.class);
//Adding the listener for the rootContext
servletContext.addListener(new ContextLoaderListener(rootContext));
//Registering the dispatcher servlet mappings.
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("dispatcher", new DispatcherServlet(rootContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
2. The #Configuration class of web application holds the beans and other setups.
#EnableWebMvc
#Configuration
#ComponentScan(basePackages={"com.abcprocure.servicerepo.controller", "com.abcprocure.servicerepo.model", "com.abcprocure.servicerepo.service"})
public class DispatcherConfig extends WebMvcConfigurerAdapter {
//Registers the url paths for resources to skip from spring. Eg. JS, CSS and images.
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// TODO Auto-generated method stub
registry.addResourceHandler("/js/**").addResourceLocations("/js/**");
registry.addResourceHandler("/html/**").addResourceLocations("/html/**");
}
//Defines the ViewResolver that Spring will use to render the views.
#Bean
public ViewResolver viewResolver() {
System.out.println("Inside View Resolver...");
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
//Defines the DataSource to use in the application.
#Bean
public DataSource dataSource() {
System.out.println("Inside DataSource bean creation....");
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
dataSource.setUrl("jdbc:sqlserver://192.168.100.131;databaseName=test");
dataSource.setUsername("egptender");
dataSource.setPassword("egp#123");
return dataSource;
}
//Defines the Hibernate's SessionFactory.
#Bean
public SessionFactory sessionFactory() {
LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(dataSource()).addAnnotatedClasses(Services.class, Operations.class, OperationParameters.class, ServiceModels.class, Businesslogic.class,TblFormMaster.class,TblFormBuilder.class);
builder.setProperty("hibernate.dialect", "org.hibernate.dialect.SQLServerDialect");
builder.setProperty("hibernate.show_sql", "true");
return builder.buildSessionFactory();
}
}
Hope this helps you. Cheers.
If you are using maven, check if you have correct dependencies

Categories