From: Mathieu Baudier Date: Sun, 22 Sep 2024 07:40:18 +0000 (+0200) Subject: Servlet-independent handler support working X-Git-Url: https://git.argeo.org/?a=commitdiff_plain;h=04a2c66d61db169290dbc397a951fb41f1a0aa45;p=lgpl%2Fargeo-commons.git Servlet-independent handler support working --- diff --git a/org.argeo.cms.ee/bnd.bnd b/org.argeo.cms.ee/bnd.bnd index adff33d30..592abffdb 100644 --- a/org.argeo.cms.ee/bnd.bnd +++ b/org.argeo.cms.ee/bnd.bnd @@ -2,6 +2,7 @@ Import-Package:\ org.osgi.service.http;version=0.0.0,\ org.osgi.service.http.whiteboard;version=0.0.0,\ org.osgi.framework.namespace;version=0.0.0,\ +org.eclipse.rap.service.http,\ org.argeo.cms.osgi,\ * diff --git a/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/AbstractJettyHttpContext.java b/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/AbstractJettyHttpContext.java index 76336b118..75b3c1a67 100644 --- a/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/AbstractJettyHttpContext.java +++ b/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/AbstractJettyHttpContext.java @@ -4,7 +4,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; -import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.Handler; import com.sun.net.httpserver.Authenticator; import com.sun.net.httpserver.Filter; @@ -30,7 +30,7 @@ public abstract class AbstractJettyHttpContext extends HttpContext { this.path = path; } - protected abstract ContextHandler getJettyHandler(); + protected abstract Handler getJettyHandler(); @Override public HttpHandler getHandler() { diff --git a/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/JettyHttpServer.java b/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/JettyHttpServer.java index b3ffe3ddb..24cc7d306 100644 --- a/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/JettyHttpServer.java +++ b/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/JettyHttpServer.java @@ -14,9 +14,9 @@ import org.argeo.api.cms.CmsLog; import org.argeo.api.cms.CmsState; import org.argeo.cms.CmsDeployProperty; import org.argeo.cms.http.server.HttpServerUtils; -import org.argeo.cms.jetty.ee10.ContextHandlerHttpContext; import org.argeo.cms.jetty.server.JettyHttpContext; import org.eclipse.jetty.http.UriCompliance; +import org.eclipse.jetty.http.pathmap.PathSpec; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; @@ -24,7 +24,11 @@ import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SslConnectionFactory; -import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.server.handler.PathMappingsHandler; +import org.eclipse.jetty.server.handler.ResourceHandler; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceFactory; +import org.eclipse.jetty.util.resource.Resources; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.ExecutorThreadPool; import org.eclipse.jetty.util.thread.QueuedThreadPool; @@ -59,8 +63,10 @@ public class JettyHttpServer extends HttpsServer { private final Map contexts = new TreeMap<>(); - private Handler rootContextHandler; - protected final ContextHandlerCollection contextHandlerCollection = new ContextHandlerCollection(); + private Handler rootHandler; + // protected final ContextHandlerCollection contextHandlerCollection = new + // ContextHandlerCollection(true); + PathMappingsHandler pathMappingsHandler = new PathMappingsHandler(true); private boolean started; @@ -111,15 +117,30 @@ public class JettyHttpServer extends HttpsServer { // holder // context - rootContextHandler = createRootContextHandler(); + rootHandler = createRootHandler(); // httpContext.addServlet(holder, "/*"); - if (rootContextHandler != null) - configureRootContextHandler(rootContextHandler); + if (rootHandler != null) + configureRootHandler(rootHandler); + +// if (rootContextHandler != null && !contexts.containsKey("/")) +// contextHandlerCollection.addHandler(rootContextHandler); +// server.setHandler(contextHandlerCollection); + if (rootHandler != null && !contexts.containsKey("/")) { + pathMappingsHandler.addMapping(PathSpec.from("/"), rootHandler); + } else { + ResourceFactory resourceFactory = ResourceFactory.of(server); + Resource rootResourceDir = resourceFactory.newClassLoaderResource("/static-root/"); + if (!Resources.isReadableDirectory(rootResourceDir)) + throw new IllegalStateException("Unable to find root resource"); - if (rootContextHandler != null && !contexts.containsKey("/")) - contextHandlerCollection.addHandler(rootContextHandler); + ResourceHandler rootResourceHandler = new ResourceHandler(); + rootResourceHandler.setBaseResource(rootResourceDir); + rootResourceHandler.setDirAllowed(false); + rootResourceHandler.setWelcomeFiles("index.html"); - server.setHandler(contextHandlerCollection); + pathMappingsHandler.addMapping(PathSpec.from("/"), rootResourceHandler); + } + server.setHandler(pathMappingsHandler); // // START @@ -268,7 +289,19 @@ public class JettyHttpServer extends HttpsServer { AbstractJettyHttpContext httpContext = new JettyHttpContext(this, path); contexts.put(path, httpContext); - contextHandlerCollection.addHandler(httpContext.getJettyHandler()); + Handler jettyHandler = httpContext.getJettyHandler(); + // contextHandlerCollection.addHandler(httpContext.getJettyHandler()); + // FIXME make path more robust + PathSpec pathSpec = PathSpec.from(path + "*"); + pathMappingsHandler.addMapping(pathSpec, jettyHandler); + if (isStarted()) { + // server is already started, handler has to be started explicitly + try { + jettyHandler.start(); + } catch (Exception e) { + throw new IllegalStateException("Could not start dynamically added Jetty handler", e); + } + } return httpContext; } @@ -279,14 +312,23 @@ public class JettyHttpServer extends HttpsServer { if (!contexts.containsKey(path)) throw new IllegalArgumentException("Context " + path + " does not exist"); AbstractJettyHttpContext httpContext = contexts.remove(path); - if (httpContext instanceof ContextHandlerHttpContext contextHandlerHttpContext) { - // TODO stop handler first? - // FIXME understand compatibility with Jetty 12 - // contextHandlerCollection.removeHandler(contextHandlerHttpContext.getServletContextHandler()); - } else { - // FIXME apparently servlets cannot be removed in Jetty, we should replace the - // handler + Handler jettyHandler = httpContext.getJettyHandler(); + if (jettyHandler.isStarted()) { + try { + jettyHandler.stop(); + } catch (Exception e) { + log.error("Cannot stop Jetty handler " + path, e); + } } + +// if (httpContext instanceof ContextHandlerHttpContext contextHandlerHttpContext) { +// // TODO stop handler first? +// // FIXME understand compatibility with Jetty 12 +// // contextHandlerCollection.removeHandler(contextHandlerHttpContext.getServletContextHandler()); +// } else { +// // FIXME apparently servlets cannot be removed in Jetty, we should replace the +// // handler +// } } @Override @@ -342,17 +384,17 @@ public class JettyHttpServer extends HttpsServer { return httpsConnector.getLocalPort(); } - protected Handler createRootContextHandler() { + protected Handler createRootHandler() { return null; } - protected void configureRootContextHandler(Handler servletContextHandler) { + protected void configureRootHandler(Handler jettyHandler) { } // TODO protect it? - public Handler getRootContextHandler() { - return rootContextHandler; + public Handler getRootHandler() { + return rootHandler; } public void setCmsState(CmsState cmsState) { @@ -371,11 +413,11 @@ public class JettyHttpServer extends HttpsServer { public static void main(String... args) { JettyHttpServer httpServer = new JettyHttpServer(); System.setProperty("argeo.http.port", "8080"); - httpServer.createContext("/", (exchange) -> { + + httpServer.createContext("/hello", (exchange) -> { exchange.getResponseBody().write("Hello World!".getBytes()); }); - httpServer.start(); - httpServer.createContext("/sub/context", (exchange) -> { + httpServer.createContext("/subcontext", (exchange) -> { final String key = "count"; Integer count = (Integer) exchange.getHttpContext().getAttributes().get(key); if (count == null) @@ -388,5 +430,6 @@ public class JettyHttpServer extends HttpsServer { sb.append(" relativePath=" + HttpServerUtils.relativize(exchange)); exchange.getResponseBody().write(sb.toString().getBytes()); }); + httpServer.start(); } } diff --git a/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/ee10/CmsJettyServer.java b/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/ee10/CmsJettyServer.java index aa3731496..ca8e5d8d0 100644 --- a/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/ee10/CmsJettyServer.java +++ b/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/ee10/CmsJettyServer.java @@ -39,7 +39,7 @@ public class CmsJettyServer extends JettyHttpServer { } @Override - protected ServletContextHandler createRootContextHandler() { + protected ServletContextHandler createRootHandler() { ServletContextHandler servletContextHandler = new ServletContextHandler(); servletContextHandler.setAttribute(INTERNAL_CONTEXT_CLASSLOADER, Thread.currentThread().getContextClassLoader()); @@ -70,7 +70,7 @@ public class CmsJettyServer extends JettyHttpServer { } @Override - protected void configureRootContextHandler(Handler servletContextHandler) { + protected void configureRootHandler(Handler servletContextHandler) { addServlets((ServletContextHandler) servletContextHandler); } diff --git a/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/ee10/ServletHttpContext.java b/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/ee10/ServletHttpContext.java index b8d6cf882..d7bae5dfd 100644 --- a/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/ee10/ServletHttpContext.java +++ b/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/ee10/ServletHttpContext.java @@ -30,7 +30,7 @@ public class ServletHttpContext extends AbstractJettyHttpContext { public ServletHttpContext(JettyHttpServer httpServer, String path) { super(httpServer, path); - ServletContextHandler rootContextHandler = (ServletContextHandler) httpServer.getRootContextHandler(); + ServletContextHandler rootContextHandler = (ServletContextHandler) httpServer.getRootHandler(); HttpContextServlet servlet = new HttpContextServlet(this); rootContextHandler.addServlet(new ServletHolder(servlet), path + "*"); } @@ -60,7 +60,7 @@ public class ServletHttpContext extends AbstractJettyHttpContext { @Override protected ServletContextHandler getJettyHandler() { - return (ServletContextHandler) getJettyHttpServer().getRootContextHandler(); + return (ServletContextHandler) getJettyHttpServer().getRootHandler(); } } diff --git a/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/ee8/CmsJavaxJettyServer.java b/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/ee8/CmsJavaxJettyServer.java index b518877e6..9abc00d08 100644 --- a/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/ee8/CmsJavaxJettyServer.java +++ b/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/ee8/CmsJavaxJettyServer.java @@ -42,7 +42,7 @@ public class CmsJavaxJettyServer extends JettyHttpServer { } @Override - protected Handler createRootContextHandler() { + protected Handler createRootHandler() { ServletContextHandler servletContextHandler = new ServletContextHandler(); servletContextHandler.setAttribute(INTERNAL_CONTEXT_CLASSLOADER, Thread.currentThread().getContextClassLoader()); @@ -74,7 +74,7 @@ public class CmsJavaxJettyServer extends JettyHttpServer { // } @Override - protected void configureRootContextHandler(Handler servletContextHandler) { + protected void configureRootHandler(Handler servletContextHandler) { addServlets(rootContextHandler); } diff --git a/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/server/HttpContextJettyContextHandler.java b/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/server/HttpContextJettyContextHandler.java index 5a768dd82..26b33c640 100644 --- a/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/server/HttpContextJettyContextHandler.java +++ b/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/server/HttpContextJettyContextHandler.java @@ -8,7 +8,8 @@ import com.sun.net.httpserver.HttpContext; class HttpContextJettyContextHandler extends ContextHandler { public HttpContextJettyContextHandler(HttpContext httpContext) { - super(new HttpContextJettyHandler(httpContext), httpContext.getPath()); + // FIXME make path more robust + super(new HttpContextJettyHandler(httpContext), httpContext.getPath() + "/*"); } } diff --git a/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/server/JettyHttpContext.java b/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/server/JettyHttpContext.java index 92411835e..e530c310a 100644 --- a/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/server/JettyHttpContext.java +++ b/org.argeo.cms.lib.jetty/src/org/argeo/cms/jetty/server/JettyHttpContext.java @@ -1,10 +1,12 @@ package org.argeo.cms.jetty.server; +import java.util.HashMap; import java.util.Map; import org.argeo.cms.jetty.AbstractJettyHttpContext; import org.argeo.cms.jetty.ContextHandlerAttributes; import org.argeo.cms.jetty.JettyHttpServer; +import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.handler.ContextHandler; import com.sun.net.httpserver.HttpContext; @@ -14,18 +16,26 @@ import com.sun.net.httpserver.HttpContext; * the jakarta/javax servlet APIs). */ public class JettyHttpContext extends AbstractJettyHttpContext { - private final ContextHandler contextHandler; - private final ContextHandlerAttributes attributes; + private final Handler handler; + private Map attributes; public JettyHttpContext(JettyHttpServer httpServer, String path) { super(httpServer, path); - contextHandler = new HttpContextJettyContextHandler(this); - attributes = new ContextHandlerAttributes(contextHandler); + boolean useContextHandler = false; + if (useContextHandler) { + // TODO not working yet + // (sub contexts do not work) + handler = new HttpContextJettyContextHandler(this); + attributes = new ContextHandlerAttributes((ContextHandler) handler); + } else { + handler = new HttpContextJettyHandler(this); + attributes = new HashMap<>(); + } } @Override - protected ContextHandler getJettyHandler() { - return contextHandler; + protected Handler getJettyHandler() { + return handler; } @Override diff --git a/org.argeo.cms.lib.jetty/src/static-root/index.html b/org.argeo.cms.lib.jetty/src/static-root/index.html new file mode 100644 index 000000000..e69de29bb diff --git a/org.argeo.cms/src/org/argeo/cms/http/server/HttpServerUtils.java b/org.argeo.cms/src/org/argeo/cms/http/server/HttpServerUtils.java index 9d2c1930a..e3def9d08 100644 --- a/org.argeo.cms/src/org/argeo/cms/http/server/HttpServerUtils.java +++ b/org.argeo.cms/src/org/argeo/cms/http/server/HttpServerUtils.java @@ -31,7 +31,7 @@ public class HttpServerUtils { Objects.requireNonNull(fullPath); String contextPath = httpContext.getPath(); if (!fullPath.startsWith(contextPath)) - throw new IllegalArgumentException(fullPath + " does not belong to context" + contextPath); + throw new IllegalArgumentException(fullPath + " does not belong to context " + contextPath); String path = fullPath.substring(contextPath.length()); // TODO optimise? if (!startWithSlash && path.startsWith(SLASH)) { diff --git a/osgi/equinox/org.argeo.cms.lib.equinox/src/org/argeo/cms/equinox/http/jetty/JavaxJettyServer.java b/osgi/equinox/org.argeo.cms.lib.equinox/src/org/argeo/cms/equinox/http/jetty/JavaxJettyServer.java index afa61dec7..34fdd3e63 100644 --- a/osgi/equinox/org.argeo.cms.lib.equinox/src/org/argeo/cms/equinox/http/jetty/JavaxJettyServer.java +++ b/osgi/equinox/org.argeo.cms.lib.equinox/src/org/argeo/cms/equinox/http/jetty/JavaxJettyServer.java @@ -17,7 +17,6 @@ import javax.servlet.http.HttpSessionListener; import org.argeo.api.cms.CmsLog; import org.argeo.cms.jetty.ee10.CmsJettyServer; import org.argeo.cms.jetty.ee8.CmsJavaxJettyServer; -import org.eclipse.equinox.http.servlet.HttpServiceServlet; import org.eclipse.jetty.ee8.nested.SessionHandler; import org.eclipse.jetty.ee8.servlet.ServletContextHandler; import org.eclipse.jetty.ee8.servlet.ServletHolder; @@ -47,7 +46,8 @@ public class JavaxJettyServer extends CmsJavaxJettyServer { } public static class InternalHttpServiceServlet implements HttpSessionListener, HttpSessionIdListener, Servlet { - private final Servlet httpServiceServlet = new HttpServiceServlet(); + // FIXME dependency to equinox.http + private final Servlet httpServiceServlet = null;// = new HttpServiceServlet(); private ClassLoader contextLoader; private final Method sessionDestroyed; private final Method sessionIdChanged;