Serving static files from alternate path in embedded Jetty
What you need:
- A DefaultServlet at "/" (recommended, it is a requirement of the servlet spec)
- this should be at named dispatcher of "default" (another requirement of servlet spec)
- An alternate DefaultServlet with your custom static content, configured via init-params
- Using a different named dispatcher than "default" (to avoid a name collision between other servlet spec features)
- You can use the
ServletHolder.setInitParameter(name,value)
to accomplish this - Be sure you set the
pathInfoOnly
parameter totrue
(to get around special cases for "default" named dispatchers)
- A servlet of your own, serving dynamic content.
AltDefaultServlet.java
package jetty.demo;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
public class AltDefaultServlet
{
public static void main(String[] args)
{
System.setProperty("org.eclipse.jetty.LEVEL","INFO");
Server server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(8080);
server.addConnector(connector);
// The filesystem paths we will map
String homePath = System.getProperty("user.home");
String pwdPath = System.getProperty("user.dir");
// Setup the basic application "context" for this application at "/"
// This is also known as the handler tree (in jetty speak)
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setBaseResource(Resource.newResource(pwdPath));
context.setContextPath("/");
server.setHandler(context);
// add a simple Servlet at "/dynamic/*"
ServletHolder holderDynamic = new ServletHolder("dynamic", DynamicServlet.class);
context.addServlet(holderDynamic, "/dynamic/*");
// add special pathspec of "/home/" content mapped to the homePath
ServletHolder holderHome = new ServletHolder("static-home", DefaultServlet.class);
holderHome.setInitParameter("resourceBase",homePath);
holderHome.setInitParameter("dirAllowed","true");
holderHome.setInitParameter("pathInfoOnly","true");
context.addServlet(holderHome,"/home/*");
// Lastly, the default servlet for root content (always needed, to satisfy servlet spec)
// It is important that this is last.
ServletHolder holderPwd = new ServletHolder("default", DefaultServlet.class);
holderPwd.setInitParameter("dirAllowed","true");
context.addServlet(holderPwd,"/");
try
{
server.start();
server.dump(System.err);
server.join();
}
catch (Throwable t)
{
t.printStackTrace(System.err);
}
}
}
Serving static files with content negotiation using embedded Jetty
You are not serving static files if you want to use content negotiation.
Content Negotiation is a fundamental concept of HTTP, and it really for the content that is being served from the (hand waving) "resource", that you are requesting.
Serving static files is a specialized form of "resource", where the mime-type / content-type is known based on its file extension.
The DefaultServlet (which is doing the static file serving in your example) has 1 more feature on top of the this specialization, but not for content-type, but rather content-encoding (you can pre-compress your static resources by creating a <filename>.gz
file that sits next to the original file, if the requesting client indicates that they can accept gzip, then this <filename>.gz
is served instead of the uncompressed <filename>
version.
In order to accomplish this goal, you'll need to write something that serves the static files in a way that makes sense for you.
What you'll need if you want to do this yourself.
- A new servlet on url-pattern
/*
- The new servlet's
.init()
creates an in-memory data structure that houses all of the known static files you have, plus their extensions, and mime-types. - The new servlet's
.doGet()
will handle any incoming requests by seeing if there is an acceptable resource to serve based on this in-memory lookup. Serve the actual content you want from this in-memory lookup. - Don't forget to support ETag, Ranged requests, Server response Cache, HTTP Cache, and Gzip.
Serving static files with jetty in karaf (outside of bundle)
The problem maybe related with pax-web version. In Karaf 3.0.5 it uses version Pax-web 3.2.6 which I have read (sorry I cannot find the link) had a bug related with serving static content.
I have tested @Achim's approach in Karaf 4.0.3 (Pax-web 4.2.3) and it works like charm.
Serving static files with embedded Jetty
Use a ResourceHandler
instead of ServletContextHandler
.
Unable to access static resources in JAR file using embedded Jetty
The code you have ...
String res = ApiServer.class.getClassLoader().getResource("res").toExternalForm();
context.setResourceBase(res);
Does not work for me, as you cannot use a classloader to obtain a directory reference, only file references. The call ClassLoader.getResource("res")
always returns null.
This needs to be fixed first.
Next, your declaration of Jersey is exceedingly greedy.
ServletHolder jerseyServlet = context.addServlet(ServletContainer.class, "/*");
jerseyServlet.setInitOrder(0);
This means that Servlet (ServletContainer.class
) is handling 100% of all requests, even requests for static content.
It is impossible for that Servlet, based on your url-pattern, to "not handle" static requests and let Jetty serve those static requests.
Relax this url-pattern, to say /api/*
and then you'll be one step closer.
The final thing you need is a DefaultServlet
(the component in the Servlet spec, and Jetty that serves static files).
So you'll wind up with the following code ...
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
URL url = ApiServer.class.getClassLoader().getResource("res/index.html");
if (url == null)
throw new FileNotFoundException("Whoops, can't find static resources folder");
URI webroot = url.toURI().resolve("./");
context.setBaseResource(Resource.newResource(webroot));
ServletHolder jerseyServlet = context.addServlet(ServletContainer.class, "/api/*");
jerseyServlet.setInitOrder(0);
// Tells the Jersey Servlet which REST service/class to load.
String classes = new StringJoiner(",")
.add(MyClass1.class.getCanonicalName())
.add(MyClass2.class.getCanonicalName())
.toString();
jerseyServlet.setInitParameter(ServerProperties.PROVIDER_CLASSNAMES, classes);
// always named "default", always last, always on url-pattern "/"
ServletHolder defaultServ = new ServletHolder("default", DefaultServlet.class);
defaultServ.setInitParameter("dirAllowed","true");
context.addServlet(defaultServ,"/");
jettyServer = new Server(port);
jettyServer.setHandler(context);
Deploy Static content in jetty
Note: be careful with your DOCTYPE, what you have declared is from Jetty 7.x thru Jetty 8.x, and is not correct for Jetty 9.x
Don't mix ResourceHandler and WebAppContext / ServletContextHandler.
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN"
"http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
<Set name="contextPath">/mail</Set>
<Set name="virtualHosts">
<Array type="java.lang.String">
<Item>apps.cairunet.ad.br</Item>
</Array>
</Set>
</Configure>
The most basic support is to not reference /ccmail
in your <Configure>
.
The fact that it exists as ${jetty.base}/webapps/ccmail/
is enough, that will deploy /ccmail
as a static resource base for you.
BUT if you want to combine static resources with virtual hosts, then you can either use a WebAppContext with an alternate base, or a new ResourceHandler.
Example of alternate bases:
Serving static files from alternate path in embedded Jetty
Example of ResourceHandler usage:
https://www.eclipse.org/jetty/documentation/current/static-content-deployment.html
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN"
"http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure class="org.eclipse.jetty.server.handler.ContextHandler">
<Set name="contextPath">/ccmail</Set>
<Set name="handler">
<New class="org.eclipse.jetty.server.handler.ResourceHandler">
<Set name="resourceBase">/fully/qualified/path/to/my/jetty.base/webapps/ccmail</Set>
<Set name="directoriesListed">true</Set>
</New>
</Set>
<Set name="virtualHosts">
<Array type="java.lang.String">
<Item>apps.cairunet.ad.br</Item>
</Array>
</Set>
</Configure>
Related Topics
How to Concatenate Int Values in Java
How to Use Java.String.Format in Scala
Use of Multiple Datasources in Spring Batch
Jdbc Class.Forname VS Drivermanager.Registerdriver
How to Make My String Comparison Case-Insensitive
Com.Jcraft.Jsch.Jschexception: Unknownhostkey
Use Cases and Examples of Gof Decorator Pattern for Io
How to Set Specific Java Version to Maven
Precise Definition of "Functional Interface" in Java 8
How to Detect the Presence of Url in a String
Jre 1.7 - Java Version - Returns: Java/Lang/Noclassdeffounderror: Java/Lang/Object
Deep Clone Utility Recommendation
How to Remove Entity with Manytomany Relationship in JPA (And Corresponding Join Table Rows)