3

I am using jetty v9.2.10.v20150310 with java version "1.8.0_45" on a linux box with a 3.18.9 kernel.

The problem is that if I set the context path to a non root value; i.e., /embed I can access my web page on my embedded jetty server. However if I set the context path to root; i.e., "/" I cannot access the page. Interestingly this problem does not show up when I interact with the servlet via curl.

Here is the code:

final ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);

final String servletWebDir = "/";
servletHandler.setContextPath( servletWebDir );

final customServlet iass = new customServlet();

final ServletHolder servletHolder = new ServletHolder( iass );
servletHolder.setInitOrder(0);
servletHandler.addServlet( servletHolder, "/customServlet" );

final ResourceHandler resourceHandler = new ResourceHandler();
resourceHandler.setDirectoriesListed(false);
resourceHandler.setResourceBase(".");

final HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[]{ servletHandler, resourceHandler } );

server.setHandler( handlers );

If I change servletWebDir from "/" to "/embed" everything works as it should. If not I get a 404.

I can interact successfully with the servlet via curl commands a follows:

curl http://host:8080/customServlet?command=exp

If I attempt to use http://host:8080/customServlet in either firefox or chrome with servletWebDir set to "/" I get a 404. Note that this code works fine under jetty v8.1.16.v20140903.

What am I doing wrong? What have I missed in Jetty v9.x?

Updated code that uses setBaseResource and drops the ResourceHandler:

final ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
final String servletWebDir = "/";
final String theBaseResourceDir = "/aa/bb/cc";
Resource theBaseResource = null;
try{
    theBaseResource = Resource.newResource( theBaseResourceDir );
}
catch( MalformedURLException e ){
    System.err.println( "setup failed on newResource with the exception " + e.toString() );
    System.exit(0);
}

servletHandler.setBaseResource( theBaseResource );
System.err.println("Class path->" + servletHandler.getClassPath() );

final customServlet iass = new customServlet();
final ServletHolder servletHolder = new ServletHolder( iass );
servletHolder.setInitOrder(0);
servletHandler.addServlet( servletHolder, "/customServlet" );
final HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[]{ servletHandler } );
server.setHandler( handlers );

This new code no longer serves a static page to a web browser regardless of the value of servletWebDir. Interacting with the custom servlet via curl still works. If the above new code is correct have I missed something? The class path is reported as null in the error logs. What can I try next?

Joakim:

I tried the code you suggested. I really appreciate the time and effort you took to prepare the code sample. However the code fails at run time. The error log states:

STDERR: 2015-05-09 15:51:32.278:WARN:/embed:main: unavailable java.lang.IllegalAccessException: Class org.eclipse.jetty.server.handler.ContextHandler$Context can not access a member of class customServlet with modifiers "private" at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)

The exception does not identify the exact member that should be made public. My updated code does work and I do not have to change a whole bunch of methods from private to public.

Joakim could you please explain the benefits of your solution which, if I am not mistaken, requires changing private methods and data members to public data members and methods and therefore losing some of the benefits of encapsulation.

Updated Code that fixes this problem:

server = new Server();
final ServerConnector connector = getConnector( server );
connector.setReuseAddress(false);
connector.setSoLingerTime(0);

final int port = 8080;
connector.setHost( theHostName );
connector.setPort( port );

server.addConnector(connector);

final String theRootContextDir = "/";
final ContextHandler rootContext = new ContextHandler(theRootContextDir);
final String theBaseResourceDir = ".";
rootContext.setResourceBase( theBaseResourceDir );

final ResourceHandler rhx = new ResourceHandler();
rootContext.setHandler( rhx );

/**
  * I want to replace the default jetty error handler with my
  * custom error handler. However I have not figured out how
  * to do it under jetty v9.x, yet-(May.08.2015,W.S.)
  * final ErrorHandler uiErrHandler = new userInputErrorHandler( logger );
  * rootContext.setErrorHandler( uiErrHandler );
  ***/

final ServletContextHandler servletHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);

final String theServletContextPath = "/";
servletHandler.setContextPath( theServletContextPath );
servletHandler.setResourceBase( "." );

final customServlet iass = new customServlet();
final ServletHolder servletHolder = new ServletHolder( iass );
final MultipartConfigElement mce = new MultipartConfigElement( fileUploadTmpDir );
servletHolder.getRegistration().setMultipartConfig( mce );
servletHolder.setInitOrder(0);
final String theServletName = "/customServlet";
servletHandler.addServlet( servletHolder, theServletName );

final HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[]{ rootContext, servletHandler } );

server.setHandler( handlers );
west suhanic
  • 41
  • 1
  • 4

1 Answers1

3

You are using a ServletContextHandler, you cannot mix that with a ResourceHandler, as the built-in DefaultServlet of the ServletContextHandler will serve the files (or give an error response), leaving the ResourceHandler to never execute.

To Fix:

Drop the ResourceHandler (its far inferior to the DefaultServlet anyway).

Set the servletHandler.setBaseResource(Resource) to your web application root directory (where your static files are). This can be a URL, URI, or file system path reference.

Examples:

// As a file system reference
servletHandler.setBaseResource(Resource.newResource("/path/to/res"));

// or URL
servletHandler.setBaseResource(Resource.newResource("jar:file://tmp/b.jar!/webroot"));

The resource path should point to a directory, not a specific file.

See previous answer about this for more details.

Example:

package jetty;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.nio.file.Path;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.resource.Resource;

public class SimpleServletExample
{
    public static void main(String[] args)
    {
        try
        {
            Server server = new Server(8080);

            // Find the full path to the webroot.
            // Use the real path, with real file system case for all parts of the path
            // Otherwise we fall afoul of alias checking.
            // (esp on OSX and Windows. Unix and Linux do not have this issue)
            Path webrootPath = new File("src/test/resources/sample-files").toPath().toRealPath();

            URI webrootUri = webrootPath.toUri();

            System.err.println("webroot uri: " + webrootUri);

            Resource webroot = Resource.newResource(webrootUri);
            if (!webroot.exists())
            {
                System.err.println("Resource does not exist: " + webroot);
                System.exit(-1);
            }

            if (!webroot.isDirectory())
            {
                System.err.println("Resource is not a directory: " + webroot);
                System.exit(-1);
            }

            // Establish ServletContext for all servlets
            ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
            context.setContextPath("/");
            context.setBaseResource(webroot);
            // What file(s) should be used when client requests a directory
            context.setWelcomeFiles(new String[] { "index.html" });
            server.setHandler(context);

            // Add a servlet (technique #1)
            ServletHolder holderHello = context.addServlet(HelloServlet.class,"/hello");
            holderHello.setInitOrder(0);

            // Add default servlet last (always last) (technique #2)
            // Must be named "default", must be on path mapping "/"
            ServletHolder holderDef = new ServletHolder("default",DefaultServlet.class);
            holderDef.setInitParameter("dirAllowed","true");
            context.addServlet(holderDef,"/");

            // Start server
            server.start();
        }
        catch (MalformedURLException e)
        {
            System.err.println("Unable to establish webroot");
            e.printStackTrace(System.err);
        }
        catch (Throwable t)
        {
            t.printStackTrace(System.err);
        }
    }
}
Community
  • 1
  • 1
Joakim Erdfelt
  • 46,896
  • 7
  • 86
  • 136
  • Hello: I did as you suggested. I used setBaseResource and I removed the ResourceHandler. However it does not work even if I set servletWebDir to "/embed". Can you please make another suggestion. Thank you for your time. – west suhanic May 07 '15 at 01:50
  • 1
    @westsuhanic its in poor form to edit someones answer with more questions, use your own original question to post an update to the question. (went ahead and did it for you) – Joakim Erdfelt May 07 '15 at 17:20
  • This answer does not lead to static resources being served by `server` for me. – kleinfreund Mar 07 '17 at 11:27