javamavenjerseyembedded-jettywebjars

Maven Webjars are not found


I'm trying to use webjars for bootstrap based on their documentation

<dependencies>
    <dependency>
        <groupId>org.webjars</groupId>
        <artifactId>bootstrap</artifactId>
        <version>3.1.0</version>
    </dependency>
</dependencies>

This is how I start the server.

public static void main(String[] args) throws Exception {
        final Server server = createServer();
        try {
            server.start();
            server.join();
        } finally {
            server.destroy();
        }
    }
private static Server createServer() throws Exception {
        final int serverPort = getServerPort();

        final Server server = new Server(serverPort);
        final HandlerList  servletContextHandlers = new HandlerList();
        servletContextHandlers.addHandler(buildServletContextHandler());
        server.setHandler(servletContextHandlers);

        return server;
    }

    private static ServletContextHandler buildServletContextHandler() throws ConfigurationException {
        final ResourceConfig resourceConfig = new ResourceConfig();
        resourceConfig.register(new CustomApiBinder());
        resourceConfig.packages("com.foo.api");

        final ServletHolder servletHolder = new ServletHolder(new ServletContainer(resourceConfig));

        final ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
        servletContextHandler.setContextPath("/api");
        servletContextHandler.addServlet(servletHolder, "/*");

        return servletContextHandler;
    }

Then when I try to link the bootstrap css sheet, I get a file not found error

<link rel='stylesheet' href='webjars/bootstrap/3.1.0/css/bootstrap.min.css'>

Do I need a special handler for that? From the documentation, it says that if you use servlet 3, you don't need anything else.

Does anyone have an example without using Spring?

Jetty 9.4.9.v20180320

Jersey 2.26


Solution

  • The bootstrap-<ver>.jar has a META-INF/resources/ subdirectory.

    $ jar -tvf bootstrap-4.0.0.jar | grep META-INF/resources
         0 Thu Jan 18 21:20:32 GMT 2018 META-INF/resources/
         0 Thu Jan 18 21:20:32 GMT 2018 META-INF/resources/webjars/
         0 Thu Jan 18 21:20:32 GMT 2018 META-INF/resources/webjars/bootstrap/
         0 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/
         0 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/
         0 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/js/
     43852 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-grid-jsf.css
      4076 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-grid-jsf.css.gz
     43852 Thu Jan 18 11:29:48 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-grid.css
      4076 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-grid.css.gz
     95910 Thu Jan 18 11:29:48 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-grid.css.map
     34243 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-grid.min-jsf.css
      3483 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-grid.min-jsf.css.gz
     34243 Thu Jan 18 11:29:48 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-grid.min.css
      3483 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-grid.min.css.gz
     76209 Thu Jan 18 11:29:48 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-grid.min.css.map
    178152 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-jsf.css
     22410 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-jsf.css.gz
      4798 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-reboot-jsf.css
      1683 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-reboot-jsf.css.gz
      4798 Thu Jan 18 11:29:48 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-reboot.css
      1683 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-reboot.css.gz
     57721 Thu Jan 18 11:29:48 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-reboot.css.map
      3936 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-reboot.min-jsf.css
      1584 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-reboot.min-jsf.css.gz
      3936 Thu Jan 18 11:29:48 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-reboot.min.css
      1584 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-reboot.min.css.gz
     25881 Thu Jan 18 11:29:48 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap-reboot.min.css.map
    178152 Thu Jan 18 11:29:48 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap.css
     22410 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap.css.gz
    411645 Thu Jan 18 11:29:48 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap.css.map
    144877 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap.min-jsf.css
     20563 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap.min-jsf.css.gz
    144877 Thu Jan 18 11:29:48 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap.min.css
     20563 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap.min.css.gz
    551641 Thu Jan 18 11:29:48 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/css/bootstrap.min.css.map
    195855 Thu Jan 18 11:29:48 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/js/bootstrap.bundle.js
     41578 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/js/bootstrap.bundle.js.gz
    326634 Thu Jan 18 11:29:48 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/js/bootstrap.bundle.js.map
     67742 Thu Jan 18 11:29:48 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/js/bootstrap.bundle.min.js
     19244 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/js/bootstrap.bundle.min.js.gz
    273872 Thu Jan 18 11:29:48 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/js/bootstrap.bundle.min.js.map
    115048 Thu Jan 18 11:29:48 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/js/bootstrap.js
     20137 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/js/bootstrap.js.gz
    195373 Thu Jan 18 11:29:48 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/js/bootstrap.js.map
     48944 Thu Jan 18 11:29:48 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/js/bootstrap.min.js
     13105 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/js/bootstrap.min.js.gz
    161998 Thu Jan 18 11:29:48 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/js/bootstrap.min.js.map
       284 Thu Jan 18 21:20:32 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/webjars-requirejs.js
       182 Thu Jan 18 21:20:38 GMT 2018 META-INF/resources/webjars/bootstrap/4.0.0/webjars-requirejs.js.gz
    

    This kind of JAR is a web resource jar, and for Jetty it's only available when using a full blown WebAppContext.

    Note: When using a Jetty WebAppContext all of the WEB-INF/lib/*.jar!/META-INF/resources/ contents will be unpacked into a temporary directory so that Jetty can serve the contents of those special jar files.

    You have a few options from here (ordered from best/easiest choice to most complex).

    1. Have maven unpack your META-INF/resources jars into your resources directory, that you then reference by classpath resource URL as the ServletContextHandler resource base location.
    2. Have your own code unpack the META-INF/resources directories into a temporary directory that you then use as a the ServletContextHandler resource base directory.
    3. Change over to using a full blown WebAppContext with a war file and all of the extra configuration necessary to enable the various features you want to use.

    Note, if your eventual end goal is to build a jetty uber jar, then option 1 will be the best choice overall.

    The choice you make will depend on how you eventually want to package your project up.

    The biggest issue with META-INF/resources based content is name collision resolution. Because of this, I encourage you to go with option 1 as this will resolve (at build time) any conflict resolution issues.

    Eg: If you have 2 JAR files, both with META-INF/resources/foo.css file (but with different contents) and a request arrives for http://<host>/foo.css, which one do you serve?

    For an example of Option 1 see - https://github.com/jetty-project/embedded-jetty-with-web-resources

    Some embedded-jetty resources maintained by the Eclipse Jetty project: