4

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

Marc
  • 16,170
  • 20
  • 76
  • 119
  • Are the webjars/bootstrap using `META-INF/resources` directories? Also, where is your base resource path declaration for the `ServletContextHandler` in your code example? – Joakim Erdfelt Apr 10 '18 at 14:50
  • 1
    What do you mean by webjars/boostrap using META-INF/resouces? There's nothing in this directory. I thought maven would copy the resources in there – Marc Apr 10 '18 at 14:52

3 Answers3

3

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.

  • Will it be 1 self executing jar? Then option 1.
  • Will it be a collection of jars? Then options 1 and 2 are good choices.
  • Will it be a collection of jars and a war file? Then option 3 is a good choice.

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:

Joakim Erdfelt
  • 46,896
  • 7
  • 86
  • 136
  • Thank you for your answer. Do you have any resources about how to setup a embedded WebAppContect for an embedded server? Do I need a web.xml? – Marc Apr 10 '18 at 16:26
  • Also it's asking me to set a war for the context – Marc Apr 10 '18 at 16:29
  • updated answer with links to embedded-jetty documentation and examples – Joakim Erdfelt Apr 10 '18 at 16:40
  • Im a little bit confused. Why do we need to specify a war? Is it not what's going to be built? – Marc Apr 10 '18 at 17:29
  • like the answer says, the whole `META-INF/resources` concept is for war files (aka `WebAppContext`). You can get around that requirement by doing the work that `WebAppContext` would have done for you during build time using Options 1 or 2 in the answer. Choosing to use a war is the hardest/most difficult/most work option (as was also pointed out in the answer) – Joakim Erdfelt Apr 10 '18 at 19:03
  • Ah sorry I misunderstood – Marc Apr 10 '18 at 19:16
  • I went ahead and prepared an example for you, see "example of option 1" in answer. – Joakim Erdfelt Apr 10 '18 at 19:27
0

You could try to add a ResourceHandler like:

ResourceHandler resourceHandler = new ResourceHandler();
resourceHandler.setDirectoriesListed(false);
resourceHandler.setResourceBase(Main.class.getResource("META-INF/resources/webjars").toExternalForm());

ContextHandler webJarContext = new ContextHandler();
webJarContext.setContextPath("/webjars");
webJarContext.setHandler(resourceHandler);
James Ward
  • 29,283
  • 9
  • 49
  • 85
0

As Joakim Erdfelt suggested, I use maven to copy the resources into the target classes.

<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-dependency-plugin</artifactId>
                    <executions>
                        <execution>
                        <id>unpack</id>
                        <phase>generate-test-sources</phase>
                        <goals>
                            <goal>unpack</goal>
                        </goals>
                        <configuration>
                            <artifactItems>
                                <artifactItem>
                                    <groupId>org.webjars</groupId>
                                    <artifactId>swagger-ui</artifactId>
                                    <version>${swagger-ui.version}</version>                                    
                                    <type>jar</type>
                                    <includes>META-INF/resources/webjars/**/*</includes>
                                    <excludes>META-INF/resources/webjars/**/*index.html</excludes>
                                    <outputDirectory>${project.basedir}/target/classes/</outputDirectory>
                                    </artifactItem>
                                </artifactItems>
                            </configuration>
                        </execution>
                </executions>
            </plugin>   
        </plugins>
    </build>
Marc
  • 16,170
  • 20
  • 76
  • 119