0
votes

I have been trying different things for the past few days and I still haven't managed to get this to work.

I am trying to create a jar with embedded jetty that can be run java -jar MyApp.jar. Servlets work and a jsp file without the taglib line will work just fine, otherwise I get a 500 Server not found error:

org.apache.jasper.JasperException: The absolute uri: http://java.sun.com/jsp/jstl/core cannot be resolved in either web.xml or the jar files deployed with this application

build.gradle

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'application'

sourceCompatibility = 1.8
targetCompatibility = 1.8
mainClassName = "hello.App"

jar {
    manifest {
        attributes 'Main-Class': 'hello.App'
    }
}

task fatJar(type: Jar) {
    manifest {
        attributes 'Main-Class': 'hello.App'
    }
    baseName = project.name + '-all'
    from { configurations.compile.collect { it.isDirectory() ? it :zipTree(it) } }
    with jar
}

repositories {
    mavenCentral()
}

def jettyVersion = '9.4.5.v20170502'
dependencies {
    compile group: 'org.eclipse.jetty', name: 'jetty-webapp', version: jettyVersion
    compile group: 'org.eclipse.jetty', name: 'jetty-servlets', version: jettyVersion
    compile group: 'org.eclipse.jetty', name: 'jetty-annotations', version: jettyVersion
    compile group: 'org.eclipse.jetty', name: 'apache-jsp', version: jettyVersion
    compile group: 'org.eclipse.jetty', name: 'apache-jstl', version: jettyVersion

    compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.1.2'
    compile group: 'org.slf4j', name: 'jcl-over-slf4j', version: '1.7.7'
}

web.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="
        http://java.sun.com/xml/ns/j2ee
        http://java.sun.com/xml/ns/j2ee/web-app_3_0.xsd" version="3.0">

    <servlet>
        <servlet-name>HelloWorld</servlet-name>
        <servlet-class>hello.HelloWorldServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>HelloWorld</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

JettyServer.java

package hello.jetty;

import java.io.OutputStream;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URL;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JettyServer {
    private static final Logger log = LoggerFactory.getLogger(JettyServer.class);

    public static final String WEBAPP_RESOURCES_LOCATION = "webapp";
    private static final int DEFAULT_PORT_START = 8080;
    private static final int DEFAULT_PORT_STOP = 8090;
    private final int startPort;
    private final int stopPort;

    public JettyServer() {
        this(DEFAULT_PORT_START, DEFAULT_PORT_STOP);
    }

    public JettyServer(int startPort, int stopPort) {
        this.startPort = startPort;
        this.stopPort = stopPort;
    }

    public void start() throws Exception {
        Server server = new Server(startPort);
        WebAppContext root = new WebAppContext();

        root.setContextPath("/");
        root.setDescriptor(WEBAPP_RESOURCES_LOCATION + "/WEB-INF/web.xml");

        URL webAppDir = Thread.currentThread().getContextClassLoader().getResource(WEBAPP_RESOURCES_LOCATION);
        if (webAppDir == null) {
            throw new RuntimeException(String.format("No %s directory was found in the JAR file", WEBAPP_RESOURCES_LOCATION));
        }

        root.setResourceBase(webAppDir.toURI().toString());
        root.setParentLoaderPriority(true);

        //Enable JSP & JSTL support
        Configuration.ClassList classlist = Configuration.ClassList.setServerDefault(server);
        classlist.addAfter("org.eclipse.jetty.webapp.FragmentConfiguration",
        "org.eclipse.jetty.plus.webapp.EnvConfiguration",
        "org.eclipse.jetty.plus.webapp.PlusConfiguration");
        classlist.addBefore("org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
            "org.eclipse.jetty.annotations.AnnotationConfiguration");
        root.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",
            ".*/[^/]*servlet-api-[^/]*\\.jar$|.*/javax.servlet.jsp.jstl-.*\\.jar$|.*/[^/]*taglibs.*\\.jar$" );

        server.setHandler(root);
        server.start();

        log.info("Jetty server started");
        log.debug("Jetty web server port: {}", startPort);
        log.debug("Port to stop Jetty with the 'stop' operation: {}", stopPort);

        Monitor monitor = new Monitor(stopPort, new Server[]{server});
        monitor.start();

        server.join();

        log.info("Jetty server exited");
    }

    public void stop() {
        stop(DEFAULT_PORT_STOP);
    }

    public void stop(Integer stopPort) {
        try {
            Socket s = new Socket(InetAddress.getByName("127.0.0.1"), stopPort);
            log.info("Jetty stopping...");
            s.setSoLinger(false, 0);
            OutputStream out = s.getOutputStream();
            out.write(("stop\r\n").getBytes());
            out.flush();
            s.close();
        } catch (ConnectException e) {
            log.info("Jetty not running!");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    public static void main(String[] args) throws Exception {
        JettyServer jettyServer = new JettyServer();
        jettyServer.start();
    }
}

index.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<!DOCTYPE html>
<html lang="en">
    <body>
        <%= new java.util.Date() %>
        <br>
        <br>
        <c:out var="${1+2}" />
    </body>
</html>

How do I get jetty to display the index.jsp file?

UPDATE: Here are the jar files that gradle pulls in:

jetty-webapp-9.4.5.v20170502.jar
jetty-servlets-9.4.5.v20170502.jar
jetty-annotations-9.4.5.v20170502.jar
apache-jsp-9.4.5.v20170502.jar
apache-jstl-9.4.5.v20170502.jar
logback-classic-1.1.2.jar
jcl-over-slf4j-1.7.7.jar
jetty-xml-9.4.5.v20170502.jar
jetty-servlet-9.4.5.v20170502.jar
jetty-continuation-9.4.5.v20170502.jar
jetty-http-9.4.5.v20170502.jar
jetty-util-9.4.5.v20170502.jar
jetty-io-9.4.5.v20170502.jar
jetty-plus-9.4.5.v20170502.jar
javax.annotation-api-1.2.jar
asm-5.1.jar
asm-commons-5.1.jar
jetty-schemas-3.1.jar
javax.servlet-api-3.1.0.jar
apache-jsp-8.5.9.1.jar
ecj-4.4.2.jar
taglibs-standard-spec-1.2.5.jar
taglibs-standard-impl-1.2.5.jar
logback-core-1.1.2.jar
jetty-security-9.4.5.v20170502.jar
jetty-jndi-9.4.5.v20170502.jar
asm-tree-5.1.jar
apache-el-8.5.9.1.jar
jetty-server-9.4.5.v20170502.jar
slf4j-api-1.7.7.jar

While reading through this I noticed apache-jsp in there twice with two different versions. Could having duplicate jars be my problem? If so, how do I get gradle to exclude one of them?

1
Please list your resolved dependencies (what does gradle think your complete list of dependencies looks like?) - Joakim Erdfelt
@Joakim I have added the dependencies. - Neon612

1 Answers

0
votes

Those aren't duplicate jars, they are 2 different jars with different group ids: one from org.eclipse.jetty and one from org.mortbay.jasper.

I assume your fat jar contains all the classes etc that have been unpacked from the dependent jars. Therefore, you're going to have to add a pattern to the org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern that refers to the location of these classes inside the jar: normally that pattern contains a list of patterns of jar file names to examine for tlds, but in your case the tlds aren't contained in jar files. Furthermore, I'd double check if the classloader is a URLClassloader: jetty walks the classloader hierarchy looking for URLClassLoaders that it should interrogate and match the patterns against.

Finally, IIWY, I wouldn't do it this way :) I would pre-compile all my jsps and simply pack them into your fat jar, that way you eliminate all problems of on-the-fly discovery and compilation.