javajettyjetty-12

Porting to jetty 12, where's HandlerCollection?


In porting my app to jetty 12 I've encountered an issue: I can't find HandlerCollection or a replacement for it.

The structure of the handler tree is with a HandlerCollection containing three child Handlers, all being run sequentially:

  1. GreeterHandler, that loads context specific data (e.g. logged-in state from token cookie) for the request and appends the data to the request.
  2. ContextHandlerCollection, containing routing and children handlers for endpoints.
  3. LoggingHandler, writes to log the time the request processing took.
private void setup() {
    Server server = new Server();
    
    //...

    HandlerCollection root = new HandlerCollection();
    server.setHandler(root);

    root.addHandler(new GreeterHandler(this));

    // Create a ContextHandlerCollection to hold contexts
    ContextHandlerCollection ctxCol = new ContextHandlerCollection();
    root.addHandler(ctxCol);

    addHandler(ctxCol, "/", new IndexHandler(this));
    addHandler(ctxCol, "/submit", new SubmitHandler(this));
    addHandler(ctxCol, "/api", new ApiHandler(this));
    addHandler(ctxCol, "/user", new UserProfileHandler(this));
    addHandler(ctxCol, "/login", new LoginHandler(this));
    addHandler(ctxCol, "/logout", new LogoutHandler(this));
    addHandler(ctxCol, "/list", new ListHandler(this));

    // the rest of the /public folder
    ResourceHandler publicFilesHandler = new ResourceHandler();
    publicFilesHandler.setBaseResourceAsString("res/public/");
    publicFilesHandler.setDirAllowed(false);

    MimeTypes types = new MimeTypes.Mutable();
    types.getMimeMap().put("js", "text/javascript; charset=utf-8");
    types.getMimeMap().put("css", "text/css; charset=utf-8");
    publicFilesHandler.setMimeTypes(types);

    addHandler(ctxCol,"/files", publicFilesHandler);

    root.addHandler(new LoggingHandler(this));
}

private void addHandler(ContextHandlerCollection handlerCollection, String contextPath, Handler.Abstract handler) {
    ContextHandler newContext = new ContextHandler(contextPath);
    newContext.setHandler(handler);
    handlerCollection.addHandler(newContext);
}

I see that Handler.Sequence is close to what I'm looking for, but it will stop the request from going further down the tree when a handler returns true. The programming guide says ContextHandlerCollection will return true once it found a Handler to process the request. I expect the children of ContextHandlerCollection to return true as well. Will that not prevent the LoggingHandler from running?

How can I make sure all three children run for each request?


Solution

  • There is a migration document you can use at
    https://eclipse.dev/jetty/documentation/jetty-12/programming-guide/index.html#pg-migration-11-to-12

    The HandlerCollection was moved to org.eclipse.jetty.server.Handler.Sequence.

    But that's not appropriate for you seem to be using ContextHandler children.

    You should instead use the org.eclipse.jetty.server.handler.ContextHandlerCollection to add a context.

    But even then, using either of those is overly complicated for what they do.

    Instead, you should use the org.eclipse.jetty.server.handler.PathMappingsHandler.

    Your code would look like this ...

    Server server = new Server();
    
    //...
    
    Handler.Sequence root = new Handler.Sequence();
    server.setHandler(root);
    
    root.addHandler(new GreeterHandler(this));
    
    PathMappingsHandler pathMappingsHandler = new PathMappingsHandler();
    root.addHandler(pathMappingsHandler);
    pathMappingsHandler.addMapping(PathSpec.from("/"), new IndexHandler(this));
    pathMappingsHandler.addMapping(PathSpec.from("/submit/*"), new SubmitHandler(this));
    pathMappingsHandler.addMapping(PathSpec.from("/api/*"), new ApiHandler(this));
    pathMappingsHandler.addMapping(PathSpec.from("/user/*"), new UserProfileHandler(this));
    pathMappingsHandler.addMapping(PathSpec.from("/login/*"), new LoginHandler(this));
    pathMappingsHandler.addMapping(PathSpec.from("/logout/*"), new LogoutHandler(this));
    pathMappingsHandler.addMapping(PathSpec.from("/list/*"), new ListHandler(this));
    
    // the rest of the /public folder
    ResourceHandler publicFilesHandler = new ResourceHandler();
    publicFilesHandler.setBaseResourceAsString("res/public/");
    publicFilesHandler.setDirAllowed(false);
    
    MimeTypes types = new MimeTypes();
    types.getMimeMap().put("js", "text/javascript; charset=utf-8");
    types.getMimeMap().put("css", "text/css; charset=utf-8");
    publicFilesHandler.setMimeTypes(types);
    
    pathMappingsHandler.addMapping(PathSpec.from("/files/*"), publicFilesHandler);
    
    // TODO: you might want to implement server.setRequestLog(new MyLoggingHandler(this)) instead of the following...
    root.addHandler(new LoggingHandler(this)); 
    // this final handler will only be called for non-path requests due
    // to the existence of a root handler.   Either implement this as 
    // a Handler.Wrapper that wraps the whole request tree, or use
    // a RequestLog implementation.
    

    Note: there are 3 different PathSpec implementations that ship with Jetty.

    You can mix/match any of the above in a single PathMappingsHandler.