2
votes

The question has been asked before however the answers given either have not worked for me, or the answers sidestepped the question completely in favour of a different base example.

I am attempting to use a simple base example of a Spring MVC servlet embedded in a Jetty instance. My controllers are picking up the page requests and are returning the correct view name. It's when the jsp page is being looked for that fails. The problem, I believe, is around the Servlet Mappings, based on other answers I've seen, but I fail to see the solution in my situation.

Jetty Server Setup:

public JettyServer(int port) throws JettyServerException {
    webServerPort = port;
    webServer = new Server(webServerPort);
    webServer.setStopAtShutdown(true);
    try {
        webServer.setHandler(getServletContextHandler(getContext()));
    } catch (IOException e) {
        throw new JettyServerException("Cannot instantiate JettyServer instance", e);
    }
}

private ServletContextHandler getServletContextHandler(WebApplicationContext context) throws IOException {

    ServletContextHandler contextHandler = new ServletContextHandler();
    contextHandler.setContextPath("/");

    contextHandler.addServlet(new ServletHolder(new DispatcherServlet(context)), "/");
    contextHandler.addEventListener(new ContextLoaderListener(context));
    contextHandler.setResourceBase(new ClassPathResource("webapp").getURI().toString());

    return contextHandler;
}

private WebApplicationContext getContext() {
    AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
    context.setConfigLocation("com.company.webapptemplate.config");
    return context;
}

The Web App config class found under the package com.company.webapptemplate.config:

@Configuration
@EnableWebMvc
@ComponentScan("com.company.webapptemplate.controller")
public class WebMvcConfig extends WebMvcConfigurerAdapter {

    @Bean
    public UrlBasedViewResolver setupViewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views/");
        resolver.setSuffix(".jsp");
        resolver.setViewClass(JstlView.class);
        return resolver;
    }
}

The controller code:

@Controller
@RequestMapping("/")
public class ApplicationController {

    @RequestMapping(method = RequestMethod.GET)
    public String welcome(ModelMap model) {
        return "index";
    } 
}

The non-java classes stuff under target/classes after running a maven / IntelliJ build:

|____webapp
| |____WEB-INF
| | |____views
| | | |____index.jsp
| | |____web.xml (unused)

The response when pointing my browser to localhost:8080 after running the app in IntelliJ:

Problem accessing /WEB-INF/views/index.jsp. Reason:

    Not Found

And the smoking gun in the logs that I don't know what to do about:

2014-09-11 23:12:54 DEBUG org.springframework.web.servlet.handler.AbstractHandlerMethodMapping:297 - Looking up handler method for path /WEB-INF/views/index.jsp
2014-09-11 23:12:54 DEBUG org.springframework.web.servlet.handler.AbstractHandlerMethodMapping:305 - Did not find handler method for [/WEB-INF/views/index.jsp]
2014-09-11 23:12:54 TRACE org.springframework.web.servlet.DispatcherServlet:1101 - Testing handler map [org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping@5ddbd0c7] in DispatcherServlet with name 'org.springframework.web.servlet.DispatcherServlet-44175c06'
2014-09-11 23:12:54 TRACE org.springframework.web.servlet.handler.AbstractUrlHandlerMapping:127 - No handler mapping found for [/WEB-INF/views/index.jsp]
2014-09-11 23:12:54 TRACE org.springframework.web.servlet.DispatcherServlet:1101 - Testing handler map [org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport$EmptyHandlerMapping@a67e8f5] in DispatcherServlet with name 'org.springframework.web.servlet.DispatcherServlet-44175c06'
2014-09-11 23:12:54 TRACE org.springframework.web.servlet.DispatcherServlet:1101 - Testing handler map [org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport$EmptyHandlerMapping@2bef3229] in DispatcherServlet with name 'org.springframework.web.servlet.DispatcherServlet-44175c06'
2014-09-11 23:12:54 TRACE org.springframework.web.servlet.DispatcherServlet:1101 - Testing handler map [org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport$EmptyHandlerMapping@64c63847] in DispatcherServlet with name 'org.springframework.web.servlet.DispatcherServlet-44175c06'
2014-09-11 23:12:54 WARN  org.springframework.web.servlet.DispatcherServlet:1120 - No mapping found for HTTP request with URI [/WEB-INF/views/index.jsp] in DispatcherServlet with name 'org.springframework.web.servlet.DispatcherServlet-44175c06'

If anyone can tell me which piece of the puzzle I'm missing I'd be greatly appreciative.

Thanks.

1
Are you sure your servlet mapping is to / and not to /*? - Sotirios Delimanolis
You basically don't have JSP support at the moment so requests to *.jsp default to your DispatcherServlet which can't handle them. I don't know how to do this with jetty, but you should definitely look into how JSP integration works. - Sotirios Delimanolis

1 Answers

3
votes

Sotirios comment pushed me forward - how to get a Jsp handling servlet into the embedded Jetty....

In getServletContextHandler I changed the instantiation of contextHandler to now be a WebAppContext. All the same methods apply to this.

I then created this method to take the returned WebAppContext and set up Jsp Handling (snippet taken from here : https://github.com/jetty-project/embedded-jetty-jsp/blob/master/src/main/java/org/eclipse/jetty/demo/Main.java)

private void setupJspHandler(WebAppContext context) {

    //Ensure the jsp engine is initialized correctly
    JettyJasperInitializer sci = new JettyJasperInitializer();

    ServletContainerInitializersStarter sciStarter = new ServletContainerInitializersStarter(context);
    ContainerInitializer initializer = new ContainerInitializer(sci, null);
    List<ContainerInitializer> initializers = new ArrayList<ContainerInitializer>();
    initializers.add(initializer);
    context.setAttribute("org.eclipse.jetty.containerInitializers", initializers);
    context.addBean(sciStarter, true);

    // Set Classloader of Context to be sane (needed for JSTL)
    // JSP requires a non-System classloader, this simply wraps the
    // embedded System classloader in a way that makes it suitable
    // for JSP to use
    ClassLoader jspClassLoader = new URLClassLoader(new URL[0], this.getClass().getClassLoader());
    context.setClassLoader(jspClassLoader);

    // Add JSP Servlet (must be named "jsp")
    ServletHolder holderJsp = new ServletHolder("jsp",JspServlet.class);
    holderJsp.setInitOrder(0);
    holderJsp.setInitParameter("logVerbosityLevel","INFO");
    holderJsp.setInitParameter("fork","false");
    holderJsp.setInitParameter("xpoweredBy","false");
    holderJsp.setInitParameter("compilerTargetVM","1.7");
    holderJsp.setInitParameter("compilerSourceVM","1.7");
    holderJsp.setInitParameter("keepgenerated","true");
    context.addServlet(holderJsp, "*.jsp");
}

I imported the following in my maven pom:

<dependency>
  <groupId>org.eclipse.jetty</groupId>
  <artifactId>jetty-jsp</artifactId>
  <version>${jetty.version}</version>
</dependency>

<dependency>
  <groupId>org.eclipse.jetty</groupId>
  <artifactId>apache-jsp</artifactId>
  <version>${jetty.version}</version>
</dependency>

And also had to turn my logging down as the running of the servlet initializer dumped out logging for the WebApplicationInitializer scan as it checked the WHOLE classpath, although other questions and pages seem to give some hints on how to deal with this, for example....

https://jira.codehaus.org/browse/JETTY-1503

Now it works like a beaut'.

Thanks