I'm migrating to jetty 12, and I'm encountering some issues with the ResourceHandler. I'm using three, one for /robots.txt, one for /sitemap.xml and one for all other files at /files/.
I'm having two issues:
- /robots.txt and /sitemap.xml doesn't work. It gives a 301 and rewrites the url with appended slash (/robots.txt/) and then gives a 404.
- /files/ does send the correct files, but never sends any Content-Type headers no matter the MimeTypes I supplied
I noticed the ResourceHandler now needs to have a ContextHandler wrapped, or else I get a nullPointerException immediately. I also couldn't get ResourceHandler#setBaseResourceAsString to work at all, but it seems to work with /files/ when using ContextHandler#setBaseResourceAsString.
Any ideas what I'm doing wrong?
@Override
public void run() {
//...
// Configure jetty
jettyServer = new Server();
//...
// Calls all added handlers in list order
Handler.Sequence root = new Handler.Sequence();
jettyServer.setHandler(root);
root.addHandler(new GreeterHandler(this));
// Create a PathMappingsHandler to hold mappings
PathMappingsHandler mapHandler = new PathMappingsHandler();
root.addHandler(mapHandler);
// Add handlers to ContextHandlerCollection
// First, all dynamic handlers for user facing pages
mapHandler.addMapping(PathSpec.from(pageDescriptions.get(PAGE_INDEX).url()), new IndexHandler(this));
mapHandler.addMapping(PathSpec.from(pageDescriptions.get(PAGE_LOGIN).url()), new LoginHandler(this));
mapHandler.addMapping(PathSpec.from(pageDescriptions.get(PAGE_LOGOUT).url()), new LogoutHandler(this));
mapHandler.addMapping(PathSpec.from(pageDescriptions.get(PAGE_REGISTER).url()), new RegisterHandler(this);
mapHandler.addMapping(PathSpec.from("/robots.txt"), singleFileHandler("/robots.txt", "resources/private/robots.txt"));
mapHandler.addMapping(PathSpec.from("/sitemap.xml"), singleFileHandler("/sitemap.xml", "resources/private/sitemap.xml"));
// the rest of the /public folder
ResourceHandler publicFilesHandler = new ResourceHandler();
publicFilesHandler.setServer(jettyServer);
publicFilesHandler.setDirAllowed(true);
//publicFilesHandler.setUseFileMapping(true);
MimeTypes.Mutable types = new MimeTypes.Mutable();
types.addMimeMapping("js", "text/javascript; charset=utf-8");
types.addMimeMapping("css", "text/css; charset=utf-8");
types.addMimeMapping("svg", "image/svg+xml");
publicFilesHandler.setMimeTypes(types);
ContextHandler h = new ContextHandler(publicFilesHandler, "/files");
h.setBaseResourceAsString("resources/public/");
mapHandler.addMapping(PathSpec.from("/files/*"), h);
jettyServer.setRequestLog(new CustomRequestLog(new Slf4jRequestLogWriter(), CustomRequestLog.NCSA_FORMAT + " - %D"));
//logger.info(root.dump());
try {
jettyServer.start();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Creates a ResourceHandler that serves a single file.
*/
private Handler singleFileHandler(String contextPath, String filePath) {
ResourceHandler handler = new ResourceHandler();
handler.setServer(jettyServer);
ContextHandler result = new ContextHandler(handler);
result.setBaseResourceAsString(filePath);
return result;
}
EDIT: I also tried the following simplest setup from the docs, but I couldn't get it do produce anything other than 404s:
ResourceHandler rh = new ResourceHandler();
Resource r = ResourceFactory.of(rh).newResource("resources/public/");
if (!Resources.isReadableDirectory(r)) {
throw new IOException("Resource is not a readable directory");
}
rh.setBaseResource(r);
rh.setDirAllowed(true);
rh.setUseFileMapping(true);
rh.setServer(jettyServer);
mapHandler.addMapping(PathSpec.from("/files2/*"), rh);
A
ResourceHandlerhas aResourceas its base.That base
Resourcehas to exist, must be a directory, and must be readable by theResourceHandler.When you call
ResourceFactory.newResource("resources/public/")that call can result in a null if the resource isn't found.The string
"resources/public/"is an incomplete path that is subject to all kinds of relative path behaviors.Double check that the result of
newResource(String)is aResourceand not null.If it is null, use a
Pathobject andnewResource(Path)instead.Here's some examples to work from.
Using a
PathobjectUsing
ResourceobjectUsing
URIobjectReferencing a ClassLoader loader resource