Spring REST application configuration and startup

319 views Asked by At

I have this application that used to work just fine.

I'm trying to run it after a long while and it doesn't behave as expected.

Sorry for the not so precise error issue.

It's a Spring REST application which is exposing controllers, and it's NOT a Spring MVC application.

The first thing that puzzles me is that starting it in the debugger will not have it stop at all the breakpoints placed in the methods of the WebConfiguration class:

package com.nsn.nitro.project.rest.config;

@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
@ComponentScan(nameGenerator = PackageBeanNameGenerator.class, basePackages = { "com.nsn.nitro.project.rest" })
public class WebConfiguration extends WebMvcConfigurerAdapter {

    private static final int PAGE_DEFAULT_SIZE = 20;

    @Override
    public void addArgumentResolvers(
            List<HandlerMethodArgumentResolver> argumentResolvers) {
        PageableHandlerMethodArgumentResolver resolver = new PageableHandlerMethodArgumentResolver();
        resolver.setFallbackPageable(new PageRequest(0, PAGE_DEFAULT_SIZE));
        resolver.setMaxPageSize(50);
        resolver.setOneIndexedParameters(true);
        resolver.setPageParameterName("page");
        resolver.setSizeParameterName("size");
        argumentResolvers.add(resolver);
        super.addArgumentResolvers(argumentResolvers);
    }

    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasenames("classpath:messages/messages",
                "classpath:messages/validation");
        // If true, the key of the message will be displayed if the key is not
        // found, instead of throwing an exception
        messageSource.setUseCodeAsDefaultMessage(true);
        messageSource.setDefaultEncoding("UTF-8");
        // The value 0 means always reload the messages to be developer friendly
        messageSource.setCacheSeconds(0);
        return messageSource;
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/*").addResourceLocations("/");
    }

    @Override
    public void configureDefaultServletHandling(
            DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    @Bean
    public LocaleResolver localeResolver() {
        return new SmartLocaleResolver();
    }

    @Bean
    public MultipartResolver multipartResolver() {
        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
        multipartResolver.setMaxUploadSize(1024000);
        return multipartResolver;
    }

    // The locale interceptor provides a way to switch the language in any page
    // just by passing the lang=’en’, lang=’fr’, and so on to the url
    @Bean
    public LocaleChangeInterceptor localeChangeInterceptor() {
        LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
        localeChangeInterceptor.setParamName("lang");
        return localeChangeInterceptor;
    }

    // Avoid caching issue with AngularJS on IE
    @Bean
    public WebContentInterceptor webContentInterceptor() {
        WebContentInterceptor interceptor = new WebContentInterceptor();
        interceptor.setCacheSeconds(0);
        interceptor.setUseExpiresHeader(true);
        interceptor.setUseCacheControlHeader(true);
        interceptor.setUseCacheControlNoStore(true);
        return interceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(localeChangeInterceptor());
        registry.addInterceptor(webContentInterceptor());
    }

And the application configuration:

package com.nsn.nitro.project.rest.config;    

@Configuration
@ComponentScan(nameGenerator = PackageBeanNameGenerator.class, basePackages = {
        "com.nsn.nitro.project.data.config",
        "com.nsn.nitro.project.rest.config",
        "com.nsn.nitro.project.rest.service" })
public class ApplicationConfiguration {

    private static Logger logger = LoggerFactory.getLogger(ApplicationConfiguration.class);

    public ApplicationConfiguration() {
        logger.debug("===========>> Loading the NITRO application configuration");
    }

}

The debugger does not stop at the breakpoint sitting in the ApplicationConfiguration() constructor either. And I can't see the logger output in the console log.

The application seems to start up fine though:

INFO] <<< tomcat7-maven-plugin:2.2:run (default-cli) < process-classes @ nitro-project-rest <<<
[INFO] 
[INFO] --- tomcat7-maven-plugin:2.2:run (default-cli) @ nitro-project-rest ---
[INFO] Running war on http://localhost:8080/nitro-project-rest
[INFO] Creating Tomcat server configuration at /home/stephane/dev/java/projects/nitro-project-rest/target/tomcat
[INFO] create webapp with contextPath: /nitro-project-rest
Dec 31, 2016 9:41:14 AM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-bio-8080"]
Dec 31, 2016 9:41:14 AM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-bio-8443"]
Dec 31, 2016 9:41:16 AM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Tomcat
Dec 31, 2016 9:41:16 AM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.47
Dec 31, 2016 9:41:25 AM org.apache.catalina.core.ApplicationContext log
INFO: 1 Spring WebApplicationInitializers detected on classpath
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/home/stephane/.m2/repository/org/slf4j/slf4j-log4j12/1.6.4/slf4j-log4j12-1.6.4.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/home/stephane/.m2/repository/ch/qos/logback/logback-classic/1.1.7/logback-classic-1.1.7.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
Dec 31, 2016 9:41:26 AM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-bio-8080"]
Dec 31, 2016 9:41:26 AM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-bio-8443"]

I start the application with a Maven command:

mvn clean install tomcat7:run

But sending any request, like curl -H "Accept:application/json" --user [email protected]:******* http://localhost:8080/nitro-project-rest/greeting?message=Steph always gives me the following error:

SEVERE: Servlet.service() for servlet [default] in context with path [/nitro-project-rest] threw exception
java.lang.IllegalStateException: No WebApplicationContext found: no ContextLoaderListener or DispatcherServlet registered?

Here is my Greeting controller:

@Controller
@RequestMapping(RESTConstants.SLASH + RESTConstants.GREETING + RESTConstants.SLASH)
public class GreetingController {

    private static final String TEMPLATE = "Hello, %s!";

    @RequestMapping(value = RESTConstants.SLASH + RESTConstants.HELLO, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public HttpEntity<Greeting> hello(@RequestParam(value = "message", required = false, defaultValue = "World") String message, UriComponentsBuilder builder) {
        Greeting greeting = new Greeting(String.format(TEMPLATE, message));
        greeting.add(linkTo(methodOn(GreetingController.class).hello(message, builder)).withSelfRel());
        HttpHeaders responseHeaders = new HttpHeaders();
        responseHeaders.setLocation(builder.path(RESTConstants.SLASH + RESTConstants.GREETING + RESTConstants.SLASH + "hello").buildAndExpand().toUri());
        responseHeaders.add("Content-Type", "application/json; charset=utf-8");
        ResponseEntity<Greeting> responseEntity = new ResponseEntity<Greeting>(greeting, responseHeaders, HttpStatus.OK);        
        return responseEntity;
    }

}
1

There are 1 answers

0
Stephane On

I was missing the dispatcher servlet !

Now with this file it works much better:

public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {

    private static Logger logger = LoggerFactory.getLogger(WebInit.class);

    @Override
    protected void customizeRegistration(ServletRegistration.Dynamic registration) {
        registration.setInitParameter("dispatchOptionsRequest", "true");
        registration.setAsyncSupported(true);
        registration.setLoadOnStartup(1);
    }

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { ApplicationConfiguration.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { WebConfiguration.class };
    }

    @Override
    protected String[] getServletMappings() {
        // The '/api/*' prefix is to allow a match of an .../api/... request to a controller mapping
        // The '/' is to allow a match of a root request to a static resource
        return new String[] { "/api/*", "/" };
    }

    @Override
    protected String getServletName() {
        return CommonConstants.SERVLET_NAME;
    }

}