3

Tired of using XML files for Spring config, tried moving into a full Java approach, but I'm doubtlessly failing at it.

Just 2 classes involved:

@Configuration
public class AppInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        WebApplicationContext context = getContext();
        servletContext.addListener(new ContextLoaderListener(context));
        DispatcherServlet dispatcherServlet = new DispatcherServlet(context);
        dispatcherServlet.setDetectAllHandlerAdapters(true);
        ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet", dispatcherServlet);
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");
    }

    private AnnotationConfigWebApplicationContext getContext() {
        AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.setConfigLocation("cl.waypoint.ecobox");
        return context;
    }

    @Bean
    public RequestMappingHandlerAdapter annotationMethodHandlerAdapter() {
        final RequestMappingHandlerAdapter annotationMethodHandlerAdapter = new RequestMappingHandlerAdapter();
        List<HttpMessageConverter<?>> httpMessageConverters = new ArrayList<>();
        httpMessageConverters.add(new MappingJackson2HttpMessageConverter());
        annotationMethodHandlerAdapter.setMessageConverters(httpMessageConverters);
        return annotationMethodHandlerAdapter;
    }
}

And the Controller:

@Controller
public class Loader {

    private static final Logger LOGGER = Logger.getLogger("Loader");

    @RequestMapping("/routeLoader")
    public LoaderResult load(@RequestBody byte[] data) {
        LOGGER.info("new Route!");
        return new LoaderResult();
    }

}

And a "beautiful" red wave (Stacktrace) when using cUrl to POST some JSON data to the endpoint:

javax.servlet.ServletException: No adapter for handler [cl.waypoint.ecobox.Loader@2c4d0325]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler
    at org.springframework.web.servlet.DispatcherServlet.getHandlerAdapter(DispatcherServlet.java:1173)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:617)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1527)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1484)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:722)

Tomcat's startup log:

Información: Initializing Spring root WebApplicationContext
nov 02, 2016 12:40:57 PM org.springframework.web.context.ContextLoader initWebApplicationContext
Información: Root WebApplicationContext: initialization started
nov 02, 2016 12:40:57 PM org.springframework.web.context.support.AnnotationConfigWebApplicationContext prepareRefresh
Información: Refreshing Root WebApplicationContext: startup date [Wed Nov 02 12:40:57 ART 2016]; root of context hierarchy
nov 02, 2016 12:40:57 PM org.springframework.web.context.support.AnnotationConfigWebApplicationContext loadBeanDefinitions
Información: Found 2 annotated classes in package [cl.waypoint.ecobox]
nov 02, 2016 12:40:58 PM org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter initControllerAdviceCache
Información: Looking for @ControllerAdvice: Root WebApplicationContext: startup date [Wed Nov 02 12:40:57 ART 2016]; root of context hierarchy
nov 02, 2016 12:40:59 PM org.springframework.web.context.ContextLoader initWebApplicationContext
Información: Root WebApplicationContext: initialization completed in 1469 ms
nov 02, 2016 12:40:59 PM org.apache.catalina.core.ApplicationContext log
Información: Initializing Spring FrameworkServlet 'DispatcherServlet'
nov 02, 2016 12:40:59 PM org.springframework.web.servlet.DispatcherServlet initServletBean
Información: FrameworkServlet 'DispatcherServlet': initialization started
nov 02, 2016 12:40:59 PM org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping registerHandler
Información: Mapped URL path [/routeLoader] onto handler 'loader'
nov 02, 2016 12:40:59 PM org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping registerHandler
Información: Mapped URL path [/routeLoader.*] onto handler 'loader'
nov 02, 2016 12:40:59 PM org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping registerHandler
Información: Mapped URL path [/routeLoader/] onto handler 'loader'
nov 02, 2016 12:40:59 PM org.springframework.web.servlet.DispatcherServlet initServletBean
Información: FrameworkServlet 'DispatcherServlet': initialization completed in 152 ms

Related Maven dependencies:

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>4.3.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>4.3.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.8.4</version>
        </dependency>
    </dependencies>

Test invoker:

curl --request POST -H "Content-Type: application/json" -d '{  "NroViaje": 1,  "CodDestino": "678",  "DescDestino": "VILLA ALEMANA",  "PatenteCamion": "AA1111",  "PatenteTrailer": "BB2222",  "FechaEntrega": "2016-10-31"}' http://localhost:8080/ecobox-load-route/routeLoader

Sample sources and pom file

What could be missing?

PS: found at least 4 alike questions within Stackoverflow community, but unfortunately none of them were helpful :(

gvasquez
  • 1,919
  • 5
  • 27
  • 41
  • Why are you creating both your `ContextLoaderListener` and `DispatcherServlet` with the same context? – Sotirios Delimanolis Nov 02 '16 at 15:29
  • What is `cl.waypoint.ecobox` and what does it contain? – Sotirios Delimanolis Nov 02 '16 at 15:30
  • @SotiriosDelimanolis cl.waypoint.ecobox is the package name of the above 2 classes – gvasquez Nov 02 '16 at 15:31
  • @SotiriosDelimanolis perhaps I don't need both ContextLoaderListener and DispatcherServlet. I actually only need the servlet, but configured the listener so Spring can auto discover the Dispatcher. – gvasquez Nov 02 '16 at 15:34
  • I can't reproduce the behavior you're seeing. But get rid of your `ContextLoaderListener` and potentially `refresh` your `ApplicationContext` before passing it to the `DispatcherServlet`. Read the javadoc of `DispatcherServlet` to understand what happens if you don't. – Sotirios Delimanolis Nov 02 '16 at 16:04
  • Also don't make your `AppInitializer` be a `@Configuration` class. Separate the concerns. Create the `ApplicationContext` by referencing a separate `@Configuration` class specifically. – Sotirios Delimanolis Nov 02 '16 at 16:07
  • OK, just separated concerns...moved `@Configuration`/`@Bean` to a separate class and, commented out the `ContextLoaderListener` line. What do you exactly mean by refreshing the `ApplicationContext`? – gvasquez Nov 02 '16 at 16:12
  • It has a [refresh()](http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/support/AbstractApplicationContext.html#refresh--) method, which is important for the [`DispatcherServlet` constructor you're using](http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/DispatcherServlet.html#DispatcherServlet-org.springframework.web.context.WebApplicationContext-). – Sotirios Delimanolis Nov 02 '16 at 16:16
  • @SotiriosDelimanolis just added `context.refresh();` before the return statement in the `getContext()` method, with no further changes / improvements. I've also shared the source files as a ZIP file above. – gvasquez Nov 02 '16 at 16:28

1 Answers1

2

try,

@RequestMapping("/routeLoader")
public @ResponseBody LoaderResult load(@RequestBody byte[] data) {..}

(since you are returning LoaderResult object you may have to mark this as response body)

kuhajeyan
  • 10,727
  • 10
  • 46
  • 71