diff --git a/KEYS.txt b/KEYS.txt index acf04a8cc33..73b9fb49a4e 100644 --- a/KEYS.txt +++ b/KEYS.txt @@ -2,6 +2,6 @@ Jan Bartel AED5 EE6C 45D0 FE8D 5D1B 164F 27DE D4BF 6216 DB8F Jesse McConnell 2A68 4B57 436A 81FA 8706 B53C 61C3 351A 438A 3B7D Joakim Erdfelt 5989 BAF7 6217 B843 D66B E55B 2D0E 1FB8 FE4B 68B4 -Joakim Erdfelt B59B 67FD 7904 9843 67F9 3180 0818 D9D6 8FB6 7BAC +Joakim Erdfelt B59B 67FD 7904 9843 67F9 3180 0818 D9D6 8FB6 7BAC Joakim Erdfelt BFBB 21C2 46D7 7768 3628 7A48 A04E 0C74 ABB3 5FEA Simone Bordet 8B09 6546 B1A8 F026 56B1 5D3B 1677 D141 BCF3 584D diff --git a/VERSION.txt b/VERSION.txt index 75fe0fd2454..84b55cbe58f 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -79,6 +79,7 @@ jetty-10.0.0-alpha0 - 11 July 2019 + 2095 Remove FastCGI multiplexing + 2103 Server should open connectors early in start sequence + 2108 Update licence headers and plugin for 2018 + + 2140 Infinispan and hazelcast changes to scavenge zombie expired sessions. + 2172 Support javax.websocket 1.1 + 2175 Refactor WebSocket close handling + 2191 JPMS Support @@ -289,6 +290,7 @@ jetty-9.4.18.v20190429 - 29 April 2019 + 3609 Fix infinispan start module dependencies jetty-9.4.17.v20190418 - 18 April 2019 + + 2140 Infinispan and hazelcast changes to scavenge zombie expired sessions. + 3464 Split SslContextFactory into Client and Server + 3549 Directory Listing on Windows reveals Resource Base path + 3555 DefaultHandler Reveals Base Resource Path of each Context diff --git a/examples/embedded/pom.xml b/examples/embedded/pom.xml index c1cb601169c..cd11566a15b 100644 --- a/examples/embedded/pom.xml +++ b/examples/embedded/pom.xml @@ -132,7 +132,32 @@ org.eclipse.jetty.toolchain jetty-test-helper - + test + + + org.eclipse.jetty.websocket + jetty-websocket-client + ${project.version} + test + + + org.eclipse.jetty + jetty-distribution + ${project.version} + tar.gz + test + + + + + org.apache.maven.plugins + maven-surefire-plugin + + false + + + + diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/AsyncEchoServlet.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/AsyncEchoServlet.java index e225703dc2b..7a53073e6ee 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/AsyncEchoServlet.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/AsyncEchoServlet.java @@ -22,7 +22,6 @@ import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; import javax.servlet.AsyncContext; import javax.servlet.ReadListener; -import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.ServletOutputStream; import javax.servlet.WriteListener; @@ -35,7 +34,7 @@ public class AsyncEchoServlet extends HttpServlet private static final long serialVersionUID = 1L; @Override - protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + protected void service(HttpServletRequest request, HttpServletResponse response) throws IOException { AsyncContext asyncContext = request.startAsync(request, response); asyncContext.setTimeout(0); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/DumpServlet.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/DumpServlet.java index 38372558bd8..0f21dccf722 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/DumpServlet.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/DumpServlet.java @@ -20,6 +20,8 @@ package org.eclipse.jetty.embedded; import java.io.IOException; import java.io.PrintWriter; +import java.util.Collections; +import javax.servlet.ServletContext; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -45,12 +47,28 @@ public class DumpServlet extends HttpServlet out.println("pathInfo=" + request.getPathInfo()); out.println("session=" + request.getSession(true).getId()); + ServletContext servletContext = getServletContext(); + String r = request.getParameter("resource"); if (r != null) { - out.println("resource(" + r + ")=" + getServletContext().getResource(r)); + out.println("resource(" + r + ")=" + servletContext.getResource(r)); } + Collections.list(request.getAttributeNames()) + .stream() + .filter((name) -> name.startsWith("X-")) + .sorted() + .forEach((name) -> + out.println("request.attribute[" + name + "]=" + request.getAttribute(name))); + + Collections.list(servletContext.getAttributeNames()) + .stream() + .filter((name) -> name.startsWith("X-")) + .sorted() + .forEach((name) -> + out.println("servletContext.attribute[" + name + "]=" + servletContext.getAttribute(name))); + out.println(""); } } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServer.java index b4d5ac45fff..f003bafe00a 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServer.java @@ -28,12 +28,12 @@ import org.eclipse.jetty.servlet.ServletContextHandler; public class ExampleServer { - public static void main(String[] args) throws Exception + public static Server createServer(int port) { Server server = new Server(); ServerConnector connector = new ServerConnector(server); - connector.setPort(8080); + connector.setPort(port); server.setConnectors(new Connector[]{connector}); ServletContextHandler context = new ServletContextHandler(); @@ -45,6 +45,13 @@ public class ExampleServer handlers.setHandlers(new Handler[]{context, new DefaultHandler()}); server.setHandler(handlers); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServerXml.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServerXml.java index e65f001967a..1ca2a34440f 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServerXml.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServerXml.java @@ -18,21 +18,31 @@ package org.eclipse.jetty.embedded; +import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.xml.XmlConfiguration; /** * Configures and Starts a Jetty server from an XML declaration. - *

- * See exampleserver.xml - *

*/ public class ExampleServerXml { - public static void main(String[] args) throws Exception + public static Server createServer(int port) throws Exception { // Find Jetty XML (in classpath) that configures and starts Server. + // See src/main/resources/exampleserver.xml Resource serverXml = Resource.newSystemResource("exampleserver.xml"); - XmlConfiguration.main(serverXml.getFile().getAbsolutePath()); + XmlConfiguration xml = new XmlConfiguration(serverXml); + xml.getProperties().put("http.port", Integer.toString(port)); + Server server = (Server)xml.configure(); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + server.start(); + server.join(); } } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleUtil.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleUtil.java new file mode 100644 index 00000000000..ce4d77c8966 --- /dev/null +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleUtil.java @@ -0,0 +1,86 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import org.eclipse.jetty.util.StringUtil; + +public class ExampleUtil +{ + /** + * Get a port, possibly configured from Command line or System property. + * + * @param args the command line arguments + * @param propertyName the property name + * @param defValue the default value + * @return the configured port + */ + public static int getPort(String[] args, String propertyName, int defValue) + { + for (String arg : args) + { + if (arg.startsWith(propertyName + "=")) + { + String value = arg.substring(propertyName.length() + 2); + int port = toInt(value); + if (isValidPort(port)) + return port; + } + } + + String value = System.getProperty(propertyName); + int port = toInt(value); + if (isValidPort(port)) + return port; + + return defValue; + } + + /** + * Test if port is in the valid range to be used. + * + * @param port the port to test + * @return true if valid + */ + private static boolean isValidPort(int port) + { + return (port >= 0) && (port <= 65535); + } + + /** + * Parse an int, ignoring any {@link NumberFormatException} + * + * @param value the string value to parse + * @return the int (if parsed), or -1 if not parsed. + */ + private static int toInt(String value) + { + if (StringUtil.isBlank(value)) + return -1; + + try + { + return Integer.parseInt(value); + } + catch (NumberFormatException ignored) + { + // ignored + return -1; + } + } +} diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FastFileServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FastFileServer.java index fc5e952f443..189fbdfa5ac 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FastFileServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FastFileServer.java @@ -58,17 +58,24 @@ import org.eclipse.jetty.util.resource.Resource; */ public class FastFileServer { - public static void main(String[] args) throws Exception + public static Server createServer(int port, File resourceBase) { - Server server = new Server(8080); + Server server = new Server(port); HandlerList handlers = new HandlerList(); handlers.setHandlers(new Handler[]{ - new FastFileHandler(new File(System.getProperty("user.dir"))), + new FastFileHandler(resourceBase), new DefaultHandler() }); server.setHandler(handlers); + return server; + } + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + File directory = new File(System.getProperty("user.dir")); + Server server = createServer(port, directory); server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java index e57bc126c4f..b9d6714f2cc 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java @@ -18,11 +18,16 @@ package org.eclipse.jetty.embedded; +import java.nio.file.Path; +import java.nio.file.Paths; + import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.server.handler.ResourceHandler; +import org.eclipse.jetty.util.resource.PathResource; +import org.eclipse.jetty.util.resource.Resource; /** * Simple Jetty FileServer. @@ -30,12 +35,12 @@ import org.eclipse.jetty.server.handler.ResourceHandler; */ public class FileServer { - public static void main(String[] args) throws Exception + public static Server createServer(int port, Resource baseResource) throws Exception { // Create a basic Jetty server object that will listen on port 8080. Note that if you set this to port 0 // then a randomly available port will be assigned that you can either look in the logs for the port, // or programmatically obtain it for use in test cases. - final Server server = new Server(8080); + Server server = new Server(port); // Create the ResourceHandler. It is the object that will actually handle the request for a given file. It is // a Jetty Handler object so it is suitable for chaining with other handlers as you will see in other examples. @@ -45,13 +50,24 @@ public class FileServer // In this example it is the current directory but it can be configured to anything that the jvm has access to. resourceHandler.setDirectoriesListed(true); resourceHandler.setWelcomeFiles(new String[]{"index.html"}); - resourceHandler.setResourceBase("."); + resourceHandler.setBaseResource(baseResource); // Add the ResourceHandler to the server. HandlerList handlers = new HandlerList(); handlers.setHandlers(new Handler[]{resourceHandler, new DefaultHandler()}); server.setHandler(handlers); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Path userDir = Paths.get(System.getProperty("user.dir")); + PathResource pathResource = new PathResource(userDir); + + Server server = createServer(port, pathResource); + // Start things up! By using the server.join() the server thread will join with the current thread. // See "http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.html#join()" for more details. server.start(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServerXml.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServerXml.java index 8b37cdb6fdd..73481ef4e34 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServerXml.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServerXml.java @@ -18,6 +18,9 @@ package org.eclipse.jetty.embedded; +import java.nio.file.Path; +import java.nio.file.Paths; + import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.xml.XmlConfiguration; @@ -28,17 +31,25 @@ import org.eclipse.jetty.xml.XmlConfiguration; * This server is identical to {@link FileServer}, except that it is configured * via an {@link XmlConfiguration} config file that does the identical work. *

- *

- * See fileserver.xml - *

*/ public class FileServerXml { - public static void main(String[] args) throws Exception + public static Server createServer(int port, Path baseResource) throws Exception { + // Find Jetty XML (in classpath) that configures and starts Server. + // See src/main/resources/fileserver.xml Resource fileServerXml = Resource.newSystemResource("fileserver.xml"); XmlConfiguration configuration = new XmlConfiguration(fileServerXml); - Server server = (Server)configuration.configure(); + configuration.getProperties().put("http.port", Integer.toString(port)); + configuration.getProperties().put("fileserver.baseresource", baseResource.toAbsolutePath().toString()); + return (Server)configuration.configure(); + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Path userDir = Paths.get(System.getProperty("user.dir")); + Server server = createServer(port, userDir); server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/HelloWorld.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/HelloWorld.java index 151432fe6b9..5218c5e9249 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/HelloWorld.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/HelloWorld.java @@ -51,7 +51,8 @@ public class HelloWorld extends AbstractHandler public static void main(String[] args) throws Exception { - Server server = new Server(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = new Server(port); server.setHandler(new HelloWorld()); server.start(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/Http2Server.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/Http2Server.java index c07590e1d37..0552725c53d 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/Http2Server.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/Http2Server.java @@ -18,9 +18,12 @@ package org.eclipse.jetty.embedded; -import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.lang.management.ManagementFactory; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Arrays; import java.util.Date; import java.util.EnumSet; @@ -56,12 +59,15 @@ import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlets.PushCacheFilter; +import org.eclipse.jetty.util.resource.PathResource; import org.eclipse.jetty.util.ssl.SslContextFactory; public class Http2Server { public static void main(String... args) throws Exception { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + int securePort = ExampleUtil.getPort(args, "jetty.https.port", 8443); Server server = new Server(); MBeanContainer mbContainer = new MBeanContainer( @@ -69,10 +75,11 @@ public class Http2Server server.addBean(mbContainer); ServletContextHandler context = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS); - String docroot = "src/main/resources/docroot"; - if (!new File(docroot).exists()) - docroot = "examples/embedded/src/main/resources/docroot"; - context.setResourceBase(docroot); + Path docroot = Paths.get("src/main/resources/docroot"); + if (!Files.exists(docroot)) + throw new FileNotFoundException(docroot.toString()); + + context.setBaseResource(new PathResource(docroot)); context.addFilter(PushCacheFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); // context.addFilter(PushSessionCacheFilter.class,"/*",EnumSet.of(DispatcherType.REQUEST)); context.addFilter(PushedTilesFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); @@ -83,21 +90,21 @@ public class Http2Server // HTTP Configuration HttpConfiguration httpConfig = new HttpConfiguration(); httpConfig.setSecureScheme("https"); - httpConfig.setSecurePort(8443); + httpConfig.setSecurePort(securePort); httpConfig.setSendXPoweredBy(true); httpConfig.setSendServerVersion(true); // HTTP Connector ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpConfig), new HTTP2CServerConnectionFactory(httpConfig)); - http.setPort(8080); + http.setPort(port); server.addConnector(http); // SSL Context Factory for HTTPS and HTTP/2 - String jettyDistro = System.getProperty("jetty.distro", "../../jetty-distribution/target/distribution"); - if (!new File(jettyDistro).exists()) - jettyDistro = "jetty-distribution/target/distribution"; + Path keystorePath = Paths.get("src/main/resources/etc/keystore").toAbsolutePath(); + if (!Files.exists(keystorePath)) + throw new FileNotFoundException(keystorePath.toString()); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(jettyDistro + "/demo-base/etc/keystore"); + sslContextFactory.setKeyStorePath(keystorePath.toString()); sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR); @@ -119,7 +126,7 @@ public class Http2Server // HTTP/2 Connector ServerConnector http2Connector = new ServerConnector(server, ssl, alpn, h2, new HttpConnectionFactory(httpsConfig)); - http2Connector.setPort(8443); + http2Connector.setPort(securePort); server.addConnector(http2Connector); server.start(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JarServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JarServer.java index c84ee9b3c33..027f5f922d6 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JarServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JarServer.java @@ -18,7 +18,11 @@ package org.eclipse.jetty.embedded; -import org.eclipse.jetty.server.Handler; +import java.io.FileNotFoundException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerList; @@ -28,24 +32,36 @@ import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.resource.Resource; /** - * + * Example of serving content from a JAR file. + * The JAR file in this example does not belong to any Classpath. */ public class JarServer { - public static void main(String[] args) throws Exception + public static Server createServer(int port) throws Exception { - final Server server = new Server(8080); + Server server = new Server(port); + + Path jarFile = Paths.get("src/main/other/content.jar"); + if (!Files.exists(jarFile)) + throw new FileNotFoundException(jarFile.toString()); ServletContextHandler context = new ServletContextHandler(); Resource.setDefaultUseCaches(true); - Resource base = Resource.newResource("jar:file:src/main/resources/content.jar!/"); + Resource base = Resource.newResource("jar:" + jarFile.toAbsolutePath().toUri().toASCIIString() + "!/"); context.setBaseResource(base); context.addServlet(new ServletHolder(new DefaultServlet()), "/"); HandlerList handlers = new HandlerList(); - handlers.setHandlers(new Handler[]{context, new DefaultHandler()}); + handlers.addHandler(context); + handlers.addHandler(new DefaultHandler()); server.setHandler(handlers); + return server; + } + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java index 5f707aca97f..32d09d17c99 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JettyDistribution.java @@ -18,8 +18,9 @@ package org.eclipse.jetty.embedded; -import java.io.File; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.log.Log; @@ -41,54 +42,80 @@ public class JettyDistribution static { Path distro = asJettyDistribution(System.getProperty("jetty.home")); + LOG.debug("JettyDistribution(prop(jetty.home)) = " + distro); if (distro == null) + { distro = asJettyDistribution(System.getenv().get("JETTY_HOME")); + LOG.debug("JettyDistribution(env(JETTY_HOME)) = " + distro); + } if (distro == null) { try { - Path working = new File(".").getAbsoluteFile().getCanonicalFile().toPath(); + Path working = Paths.get(System.getProperty("user.dir")); + LOG.debug("JettyDistribution(prop(user.dir)) = " + working); while (distro == null && working != null) { distro = asJettyDistribution(working.resolve("jetty-distribution/target/distribution").toString()); working = working.getParent(); } + LOG.debug("JettyDistribution(working.resolve(...)) = " + distro); } catch (Throwable th) { LOG.warn(th); } } + + if (distro == null) + { + LOG.info("JettyDistribution() FAILURE: NOT FOUND"); + } + else + { + LOG.debug("JettyDistribution() FOUND = " + distro); + } DISTRIBUTION = distro; } - private static Path asJettyDistribution(String test) + private static Path asJettyDistribution(String jettyHome) { try { - if (StringUtil.isBlank(test)) + if (jettyHome == null) { - LOG.info("asJettyDistribution {} is blank", test); return null; } - File dir = new File(test); - if (!dir.exists() || !dir.isDirectory()) + if (StringUtil.isBlank(jettyHome)) { - LOG.info("asJettyDistribution {} is not a directory", test); + LOG.debug("asJettyDistribution {} is blank", jettyHome); return null; } - File demoBase = new File(dir, "demo-base"); - if (!demoBase.exists() || !demoBase.isDirectory()) + Path dir = Paths.get(jettyHome); + if (!Files.exists(dir)) { - LOG.info("asJettyDistribution {} has no demo-base", test); + LOG.debug("asJettyDistribution {} does not exist", jettyHome); return null; } - LOG.info("asJettyDistribution {}", dir); - return dir.getAbsoluteFile().getCanonicalFile().toPath(); + if (!Files.isDirectory(dir)) + { + LOG.debug("asJettyDistribution {} is not a directory", jettyHome); + return null; + } + + Path demoBase = dir.resolve("demo-base"); + if (!Files.exists(demoBase) || !Files.isDirectory(demoBase)) + { + LOG.debug("asJettyDistribution {} has no demo-base", jettyHome); + return null; + } + + LOG.debug("asJettyDistribution {}", dir); + return dir.toAbsolutePath(); } catch (Exception e) { @@ -97,9 +124,16 @@ public class JettyDistribution return null; } + public static Path get() + { + if (DISTRIBUTION == null) + throw new RuntimeException("jetty-distribution not found"); + return DISTRIBUTION; + } + public static Path resolve(String path) { - return DISTRIBUTION.resolve(path); + return get().resolve(path); } public static void main(String... arg) diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java index 752d071f682..fa0b53a12d1 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java @@ -18,12 +18,16 @@ package org.eclipse.jetty.embedded; -import java.io.File; +import java.io.FileNotFoundException; import java.lang.management.ManagementFactory; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import org.eclipse.jetty.annotations.AnnotationConfiguration; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.PropertiesConfigurationManager; +import org.eclipse.jetty.deploy.bindings.DebugListenerBinding; import org.eclipse.jetty.deploy.providers.WebAppProvider; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.jmx.MBeanContainer; @@ -35,6 +39,7 @@ import org.eclipse.jetty.rewrite.handler.ValidUrlRule; import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.server.AsyncRequestLogWriter; import org.eclipse.jetty.server.CustomRequestLog; +import org.eclipse.jetty.server.DebugListener; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; @@ -59,21 +64,21 @@ import org.eclipse.jetty.webapp.Configurations; */ public class LikeJettyXml { - public static void main(String[] args) throws Exception + public static Server createServer(int port, int securePort, boolean addDebugListener) throws Exception { // Path to as-built jetty-distribution directory - String jettyHomeBuild = JettyDistribution.DISTRIBUTION.toString(); + Path jettyHomeBuild = JettyDistribution.get(); // Find jetty home and base directories - String homePath = System.getProperty("jetty.home", jettyHomeBuild); - File homeDir = new File(homePath); + String homePath = System.getProperty("jetty.home", jettyHomeBuild.toString()); + Path homeDir = Paths.get(homePath); - String basePath = System.getProperty("jetty.base", homeDir + "/demo-base"); - File baseDir = new File(basePath); + String basePath = System.getProperty("jetty.base", homeDir.resolve("demo-base").toString()); + Path baseDir = Paths.get(basePath); // Configure jetty.home and jetty.base system properties - String jettyHome = homeDir.getAbsolutePath(); - String jettyBase = baseDir.getAbsolutePath(); + String jettyHome = homeDir.toAbsolutePath().toString(); + String jettyBase = baseDir.toAbsolutePath().toString(); System.setProperty("jetty.home", jettyHome); System.setProperty("jetty.base", jettyBase); @@ -91,7 +96,7 @@ public class LikeJettyXml // HTTP Configuration HttpConfiguration httpConfig = new HttpConfiguration(); httpConfig.setSecureScheme("https"); - httpConfig.setSecurePort(8443); + httpConfig.setSecurePort(securePort); httpConfig.setOutputBufferSize(32768); httpConfig.setRequestHeaderSize(8192); httpConfig.setResponseHeaderSize(8192); @@ -105,11 +110,6 @@ public class LikeJettyXml handlers.setHandlers(new Handler[]{contexts, new DefaultHandler()}); server.setHandler(handlers); - // Extra options - server.setDumpAfterStart(true); - server.setDumpBeforeStop(false); - server.setStopAtShutdown(true); - // === jetty-jmx.xml === MBeanContainer mbContainer = new MBeanContainer( ManagementFactory.getPlatformMBeanServer()); @@ -118,24 +118,21 @@ public class LikeJettyXml // === jetty-http.xml === ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpConfig)); - http.setPort(8080); + http.setPort(port); http.setIdleTimeout(30000); server.addConnector(http); // === jetty-https.xml === // SSL Context Factory + Path keystorePath = Paths.get("src/main/resources/etc/keystore").toAbsolutePath(); + if (!Files.exists(keystorePath)) + throw new FileNotFoundException(keystorePath.toString()); SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(jettyHome + "/../../../jetty-server/src/test/config/etc/keystore"); + sslContextFactory.setKeyStorePath(keystorePath.toString()); sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); - sslContextFactory.setTrustStorePath(jettyHome + "/../../../jetty-server/src/test/config/etc/keystore"); + sslContextFactory.setTrustStorePath(keystorePath.toString()); sslContextFactory.setTrustStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); - sslContextFactory.setExcludeCipherSuites("SSL_RSA_WITH_DES_CBC_SHA", - "SSL_DHE_RSA_WITH_DES_CBC_SHA", "SSL_DHE_DSS_WITH_DES_CBC_SHA", - "SSL_RSA_EXPORT_WITH_RC4_40_MD5", - "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", - "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", - "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"); // SSL HTTP Configuration HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig); @@ -145,14 +142,17 @@ public class LikeJettyXml ServerConnector sslConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), new HttpConnectionFactory(httpsConfig)); - sslConnector.setPort(8443); + sslConnector.setPort(securePort); server.addConnector(sslConnector); // === jetty-deploy.xml === DeploymentManager deployer = new DeploymentManager(); - //DebugListener debug = new DebugListener(System.out,true,true,true); - // server.addBean(debug); - // deployer.addLifeCycleBinding(new DebugListenerBinding(debug)); + if (addDebugListener) + { + DebugListener debug = new DebugListener(System.err, true, true, true); + server.addBean(debug); + deployer.addLifeCycleBinding(new DebugListenerBinding(debug)); + } deployer.setContexts(contexts); deployer.setContextAttribute( "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", @@ -208,6 +208,20 @@ public class LikeJettyXml login.setHotReload(false); server.addBean(login); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + int securePort = ExampleUtil.getPort(args, "jetty.https.port", 8443); + Server server = createServer(port, securePort, true); + + // Extra options + server.setDumpAfterStart(true); + server.setDumpBeforeStop(false); + server.setStopAtShutdown(true); + // Start the server server.start(); server.join(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java index fa8f86f1c97..351d853c9fb 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyConnectors.java @@ -18,8 +18,10 @@ package org.eclipse.jetty.embedded; -import java.io.File; import java.io.FileNotFoundException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.server.Connector; @@ -36,23 +38,13 @@ import org.eclipse.jetty.util.ssl.SslContextFactory; */ public class ManyConnectors { - public static void main(String[] args) throws Exception + public static Server createServer(int plainPort, int securePort) throws Exception { // Since this example shows off SSL configuration, we need a keystore - // with the appropriate key. These lookup of jetty.home is purely a hack - // to get access to a keystore that we use in many unit tests and should - // probably be a direct path to your own keystore. - - String jettyDistKeystore = "../../jetty-distribution/target/distribution/demo-base/etc/test-keystore"; - String keystorePath = System.getProperty("example.keystore", jettyDistKeystore); - File keystoreFile = new File(keystorePath); - if (!keystoreFile.exists()) - { - keystorePath = "jetty-distribution/target/distribution/demo-base/etc/keystore"; - keystoreFile = new File(keystorePath); - if (!keystoreFile.exists()) - throw new FileNotFoundException(keystoreFile.getAbsolutePath()); - } + // with the appropriate key. + Path keystorePath = Paths.get("src/main/resources/etc/keystore").toAbsolutePath(); + if (!Files.exists(keystorePath)) + throw new FileNotFoundException(keystorePath.toString()); // Create a basic jetty server object without declaring the port. Since // we are configuring connectors directly we'll be setting ports on @@ -67,7 +59,7 @@ public class ManyConnectors // done. The port for secured communication is also set here. HttpConfiguration httpConfig = new HttpConfiguration(); httpConfig.setSecureScheme("https"); - httpConfig.setSecurePort(8443); + httpConfig.setSecurePort(securePort); httpConfig.setOutputBufferSize(32768); // HTTP connector @@ -77,7 +69,7 @@ public class ManyConnectors // configure an idle timeout. ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpConfig)); - http.setPort(8080); + http.setPort(plainPort); http.setIdleTimeout(30000); // SSL Context Factory for HTTPS @@ -88,7 +80,7 @@ public class ManyConnectors // keystore to be used. SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath()); + sslContextFactory.setKeyStorePath(keystorePath.toString()); sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); @@ -118,7 +110,7 @@ public class ManyConnectors ServerConnector https = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), new HttpConnectionFactory(httpsConfig)); - https.setPort(8443); + https.setPort(securePort); https.setIdleTimeout(500000); // Here you see the server having multiple connectors registered with @@ -132,7 +124,14 @@ public class ManyConnectors // Set a handler server.setHandler(new HelloHandler()); + return server; + } + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + int securePort = ExampleUtil.getPort(args, "jetty.https.port", 8443); + Server server = createServer(port, securePort); // Start the server server.start(); server.dumpStdErr(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyContexts.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyContexts.java index 944cce31853..d64735e964f 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyContexts.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyContexts.java @@ -18,39 +18,42 @@ package org.eclipse.jetty.embedded; -import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; public class ManyContexts { - public static void main(String[] args) throws Exception + public static Server createServer(int port) { - final Server server = new Server(8080); + Server server = new Server(port); ContextHandler context = new ContextHandler("/"); context.setContextPath("/"); context.setHandler(new HelloHandler("Root Hello")); ContextHandler contextFR = new ContextHandler("/fr"); - contextFR.setHandler(new HelloHandler("Bonjoir")); + contextFR.setHandler(new HelloHandler("Bonjour")); ContextHandler contextIT = new ContextHandler("/it"); - contextIT.setHandler(new HelloHandler("Bongiorno")); + contextIT.setHandler(new HelloHandler("Buongiorno")); ContextHandler contextV = new ContextHandler("/"); contextV.setVirtualHosts(new String[]{"127.0.0.2"}); contextV.setHandler(new HelloHandler("Virtual Hello")); - ContextHandlerCollection contexts = new ContextHandlerCollection(); - contexts.setHandlers(new Handler[]{ - context, contextFR, contextIT, - contextV - }); + ContextHandlerCollection contexts = new ContextHandlerCollection( + context, contextFR, contextIT, contextV + ); server.setHandler(contexts); + return server; + } + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.dumpStdErr(); server.join(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyHandlers.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyHandlers.java index 1cbc13d0d4f..e6dfce9a1b9 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyHandlers.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyHandlers.java @@ -30,6 +30,8 @@ import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.server.handler.HandlerList; @@ -99,20 +101,23 @@ public class ManyHandlers HttpServletResponse response) throws IOException, ServletException { - request.setAttribute("welcome", "Hello"); + response.setHeader("X-Welcome", "Greetings from WelcomeWrapHandler"); super.handle(target, baseRequest, request, response); } } - public static void main(String[] args) throws Exception + public static Server createServer(int port) throws IOException { - final Server server = new Server(8080); + Server server = new Server(port); // create the handlers - final Handler param = new ParamHandler(); - final HandlerWrapper wrapper = new WelcomeWrapHandler(); - final Handler hello = new HelloHandler(); - final Handler dft = new DefaultHandler(); + Handler param = new ParamHandler(); + HandlerWrapper wrapper = new WelcomeWrapHandler(); + Handler hello = new HelloHandler(); + GzipHandler gzipHandler = new GzipHandler(); + gzipHandler.setMinGzipSize(10); + gzipHandler.addIncludedMimeTypes("text/plain"); + gzipHandler.addIncludedMimeTypes("text/html"); // configure request logging File requestLogFile = File.createTempFile("demo", "log"); @@ -120,16 +125,47 @@ public class ManyHandlers server.setRequestLog(ncsaLog); // create the handler collections - HandlerCollection handlers = new HandlerCollection(); - HandlerList list = new HandlerList(); + HandlerList handlers = new HandlerList(); - // link them all together + // wrap contexts around specific handlers wrapper.setHandler(hello); - list.setHandlers(new Handler[]{param, new GzipHandler()}); - handlers.setHandlers(new Handler[]{list, dft}); + ContextHandler helloContext = new ContextHandler("/hello"); + helloContext.setHandler(wrapper); + ContextHandler paramContext = new ContextHandler("/params"); + paramContext.setHandler(param); + + ContextHandlerCollection contexts = new ContextHandlerCollection(helloContext, paramContext); + + // Wrap Contexts with GZIP + gzipHandler.setHandler(contexts); + + // Set the top level Handler List + handlers.addHandler(gzipHandler); + handlers.addHandler(new DefaultHandler()); server.setHandler(handlers); + /* At this point you have the following handler hierarchy. + * + * Server.handler: + * HandlerList + * \- GzipHandler + * | \- ContextHandlerCollection + * | \- ContextHandler ("/hello") + * | | \- WelcomeWrapHandler + * | | \- HelloHandler + * | \- ContextHandler ("/params") + * | \- ParamHandler + * \- DefaultHandler + */ + + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyServletContexts.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyServletContexts.java index 53fa7317c04..d1adc4be4c3 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyServletContexts.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyServletContexts.java @@ -29,9 +29,9 @@ import org.eclipse.jetty.servlet.ServletHolder; public class ManyServletContexts { - public static void main(String[] args) throws Exception + public static Server createServer(int port) { - Server server = new Server(8080); + Server server = new Server(port); // Setup JMX MBeanContainer mbContainer = new MBeanContainer( @@ -48,7 +48,7 @@ public class ManyServletContexts // Add servlets to root context root.addServlet(new ServletHolder(new HelloServlet("Hello")), "/"); root.addServlet(new ServletHolder(new HelloServlet("Ciao")), "/it/*"); - root.addServlet(new ServletHolder(new HelloServlet("Bonjoir")), "/fr/*"); + root.addServlet(new ServletHolder(new HelloServlet("Bonjour")), "/fr/*"); // Configure context "/other" for servlets ServletContextHandler other = new ServletContextHandler(contexts, @@ -57,6 +57,13 @@ public class ManyServletContexts other.addServlet(DefaultServlet.class.getCanonicalName(), "/"); other.addServlet(new ServletHolder(new HelloServlet("YO!")), "*.yo"); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.dumpStdErr(); server.join(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/MinimalServlets.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/MinimalServlets.java index 5ce9a24016c..76a9fe82cda 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/MinimalServlets.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/MinimalServlets.java @@ -19,7 +19,6 @@ package org.eclipse.jetty.embedded; import java.io.IOException; -import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -29,13 +28,13 @@ import org.eclipse.jetty.servlet.ServletHandler; public class MinimalServlets { - public static void main(String[] args) throws Exception + + public static Server createServer(int port) { - // Create a basic jetty server object that will listen on port 8080. // Note that if you set this to port 0 then a randomly available port // will be assigned that you can either look in the logs for the port, // or programmatically obtain it for use in test cases. - Server server = new Server(8080); + Server server = new Server(port); // The ServletHandler is a dead simple way to create a context handler // that is backed by an instance of a Servlet. @@ -51,13 +50,20 @@ public class MinimalServlets // through a web.xml @WebServlet annotation, or anything similar. handler.addServletWithMapping(HelloServlet.class, "/*"); + return server; + } + + public static void main(String[] args) throws Exception + { + // Create a basic jetty server object that will listen on port 8080. + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + // Start things up! server.start(); // The use of server.join() the will make the current thread join and - // wait until the server is done executing. - // See - // http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#join() + // wait until the server thread is done executing. server.join(); } @@ -66,11 +72,11 @@ public class MinimalServlets { @Override protected void doGet(HttpServletRequest request, - HttpServletResponse response) throws ServletException, - IOException + HttpServletResponse response) throws IOException { - response.setContentType("text/html"); response.setStatus(HttpServletResponse.SC_OK); + response.setContentType("text/html"); + response.setCharacterEncoding("utf-8"); response.getWriter().println("

Hello from HelloServlet

"); } } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneConnector.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneConnector.java index c6debe8a23b..6e183117536 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneConnector.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneConnector.java @@ -26,7 +26,7 @@ import org.eclipse.jetty.server.ServerConnector; */ public class OneConnector { - public static void main(String[] args) throws Exception + public static Server createServer(int port) throws Exception { // The Server Server server = new Server(); @@ -34,7 +34,7 @@ public class OneConnector // HTTP connector ServerConnector http = new ServerConnector(server); http.setHost("localhost"); - http.setPort(8080); + http.setPort(port); http.setIdleTimeout(30000); // Set the connector @@ -42,6 +42,13 @@ public class OneConnector // Set a handler server.setHandler(new HelloHandler()); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); // Start the server server.start(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneContext.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneContext.java index 1331e6abb55..c27556f0f1e 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneContext.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneContext.java @@ -23,9 +23,9 @@ import org.eclipse.jetty.server.handler.ContextHandler; public class OneContext { - public static void main(String[] args) throws Exception + public static Server createServer(int port) { - Server server = new Server(8080); + Server server = new Server(port); // Add a single handler on context "/hello" ContextHandler context = new ContextHandler(); @@ -35,6 +35,13 @@ public class OneContext // Can be accessed using http://localhost:8080/hello server.setHandler(context); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); // Start the server server.start(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneHandler.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneHandler.java index b9a2f942f75..10df55d687d 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneHandler.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneHandler.java @@ -22,11 +22,17 @@ import org.eclipse.jetty.server.Server; public class OneHandler { + public static Server createServer(int port) + { + Server server = new Server(port); + server.setHandler(new HelloHandler()); + return server; + } + public static void main(String[] args) throws Exception { - Server server = new Server(8080); - server.setHandler(new HelloHandler()); - + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContext.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContext.java index 720309dd917..aa468d089cb 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContext.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContext.java @@ -19,8 +19,9 @@ package org.eclipse.jetty.embedded; import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.EnumSet; -import javax.servlet.DispatcherType; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -31,38 +32,59 @@ import javax.servlet.ServletRequest; import javax.servlet.ServletRequestEvent; import javax.servlet.ServletRequestListener; import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ListenerHolder; import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.resource.PathResource; +import org.eclipse.jetty.util.resource.Resource; + +import static javax.servlet.DispatcherType.ASYNC; +import static javax.servlet.DispatcherType.REQUEST; public class OneServletContext { - public static void main(String[] args) throws Exception + public static Server createServer(int port, Resource baseResource) { - Server server = new Server(8080); + Server server = new Server(port); - ServletContextHandler context = new ServletContextHandler( - ServletContextHandler.SESSIONS); + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); context.setContextPath("/"); - context.setResourceBase(System.getProperty("java.io.tmpdir")); + context.setBaseResource(baseResource); server.setHandler(context); - // Add dump servlet - context.addServlet( - context.addServlet(DumpServlet.class, "/dump/*"), - "*.dump"); + // add hello servlet context.addServlet(HelloServlet.class, "/hello/*"); + + // Add dump servlet on multiple url-patterns + ServletHolder debugHolder = new ServletHolder("debug", DumpServlet.class); + context.addServlet(debugHolder, "/dump/*"); + context.addServlet(debugHolder, "*.dump"); + + // add default servlet (for error handling and static resources) context.addServlet(DefaultServlet.class, "/"); - context.addFilter(TestFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); - context.addFilter(TestFilter.class, "/test", EnumSet.of(DispatcherType.REQUEST, DispatcherType.ASYNC)); - context.addFilter(TestFilter.class, "*.test", EnumSet.of(DispatcherType.REQUEST, DispatcherType.INCLUDE, DispatcherType.FORWARD)); + // sprinkle in a few filters to demonstrate behaviors + context.addFilter(TestFilter.class, "/test/*", EnumSet.of(REQUEST)); + context.addFilter(TestFilter.class, "*.test", EnumSet.of(REQUEST, ASYNC)); + // and a few listeners to show other ways of working with servlets context.getServletHandler().addListener(new ListenerHolder(InitListener.class)); context.getServletHandler().addListener(new ListenerHolder(RequestListener.class)); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Path tempDir = Paths.get(System.getProperty("java.io.tmpdir")); + + Server server = createServer(port, new PathResource(tempDir)); + server.start(); server.dumpStdErr(); server.join(); @@ -71,14 +93,18 @@ public class OneServletContext public static class TestFilter implements Filter { @Override - public void init(FilterConfig filterConfig) throws ServletException + public void init(FilterConfig filterConfig) { - } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + if (response instanceof HttpServletResponse) + { + HttpServletResponse httpServletResponse = (HttpServletResponse)response; + httpServletResponse.setHeader("X-TestFilter", "true"); + } chain.doFilter(request, response); } @@ -94,6 +120,7 @@ public class OneServletContext @Override public void contextInitialized(ServletContextEvent sce) { + sce.getServletContext().setAttribute("X-Init", "true"); } @Override @@ -105,15 +132,14 @@ public class OneServletContext public static class RequestListener implements ServletRequestListener { @Override - public void requestDestroyed(ServletRequestEvent sre) + public void requestInitialized(ServletRequestEvent sre) { - + sre.getServletRequest().setAttribute("X-ReqListener", "true"); } @Override - public void requestInitialized(ServletRequestEvent sre) + public void requestDestroyed(ServletRequestEvent sre) { - } } } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextJmxStats.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextJmxStats.java index 557c4588b65..9a33e10ea69 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextJmxStats.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextJmxStats.java @@ -28,9 +28,10 @@ import org.eclipse.jetty.servlet.ServletContextHandler; public class OneServletContextJmxStats { - public static void main(String[] args) throws Exception + public static Server createServer(int port) { - Server server = new Server(8080); + Server server = new Server(port); + // Add JMX tracking to Server server.addBean(new MBeanContainer(ManagementFactory .getPlatformMBeanServer())); @@ -45,6 +46,13 @@ public class OneServletContextJmxStats // Add Connector Statistics tracking to all connectors ServerConnectionStatistics.addToAllConnectors(server); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.join(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextWithSession.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextWithSession.java index 35d18c2d848..992bfec14ea 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextWithSession.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneServletContextWithSession.java @@ -18,24 +18,29 @@ package org.eclipse.jetty.embedded; +import java.nio.file.Path; +import java.nio.file.Paths; + import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.session.DefaultSessionCache; import org.eclipse.jetty.server.session.NullSessionDataStore; import org.eclipse.jetty.server.session.SessionCache; import org.eclipse.jetty.server.session.SessionHandler; import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.util.resource.PathResource; +import org.eclipse.jetty.util.resource.Resource; public class OneServletContextWithSession { - public static void main(String[] args) throws Exception + public static Server createServer(int port, Resource baseResource) { - Server server = new Server(8080); + Server server = new Server(port); // Create a ServletContext, with a session handler enabled. ServletContextHandler context = new ServletContextHandler( ServletContextHandler.SESSIONS); context.setContextPath("/"); - context.setResourceBase(System.getProperty("java.io.tmpdir")); + context.setBaseResource(baseResource); server.setHandler(context); // Access the SessionHandler from the context. @@ -55,6 +60,15 @@ public class OneServletContextWithSession // Servlet to read/set the greeting stored in the session. // Can be accessed using http://localhost:8080/hello context.addServlet(HelloSessionServlet.class, "/"); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Path dir = Paths.get(System.getProperty("user.dir")); + PathResource baseResource = new PathResource(dir); + Server server = createServer(port, baseResource); server.start(); server.dumpStdErr(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java index a69f20f042a..0942600f5da 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java @@ -19,27 +19,20 @@ package org.eclipse.jetty.embedded; import java.io.File; -import java.lang.management.ManagementFactory; -import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.Configurations; import org.eclipse.jetty.webapp.WebAppContext; public class OneWebApp { - public static void main(String[] args) throws Exception + public static Server createServer(int port) { // Create a basic jetty server object that will listen on port 8080. // Note that if you set this to port 0 then a randomly available port // will be assigned that you can either look in the logs for the port, // or programmatically obtain it for use in test cases. - Server server = new Server(8080); - - // Setup JMX - MBeanContainer mbContainer = new MBeanContainer( - ManagementFactory.getPlatformMBeanServer()); - server.addBean(mbContainer); + Server server = new Server(port); // The WebAppContext is the entity that controls the environment in // which a web application lives and breathes. In this example the @@ -56,6 +49,13 @@ public class OneWebApp // A WebAppContext is a ContextHandler as well so it needs to be set to // the server so it is aware of where to send the appropriate requests. server.setHandler(webapp); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); Configurations.setServerDefault(server); @@ -66,7 +66,6 @@ public class OneWebApp // The use of server.join() the will make the current thread join and // wait until the server is done executing. - // See http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#join() server.join(); } } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java index 0d51c81d234..e2dcfb79e3d 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebAppWithJsp.java @@ -18,49 +18,45 @@ package org.eclipse.jetty.embedded; -import java.io.File; -import java.lang.management.ManagementFactory; +import java.io.FileNotFoundException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; import org.eclipse.jetty.annotations.AnnotationConfiguration; -import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.resource.PathResource; import org.eclipse.jetty.webapp.WebAppContext; public class OneWebAppWithJsp { - public static void main(String[] args) throws Exception + public static Server createServer(int port) throws FileNotFoundException { // Create a basic jetty server object that will listen on port 8080. // Note that if you set this to port 0 then // a randomly available port will be assigned that you can either look // in the logs for the port, // or programmatically obtain it for use in test cases. - Server server = new Server(8080); - - // Setup JMX - MBeanContainer mbContainer = new MBeanContainer( - ManagementFactory.getPlatformMBeanServer()); - server.addBean(mbContainer); + Server server = new Server(port); // The WebAppContext is the entity that controls the environment in - // which a web application lives and - // breathes. In this example the context path is being set to "/" so it + // which a web application lives and breathes. + // In this example the context path is being set to "/" so it // is suitable for serving root context - // requests and then we see it setting the location of the war. A whole - // host of other configurations are + // requests and then we see it setting the location of the war. + // A whole host of other configurations are // available, ranging from configuring to support annotation scanning in - // the webapp (through - // PlusConfiguration) to choosing where the webapp will unpack itself. + // the webapp (through PlusConfiguration), to choosing where + // the webapp will unpack itself. WebAppContext webapp = new WebAppContext(); webapp.setContextPath("/"); - File warFile = new File( - "jetty-distribution/target/distribution/demo-base/webapps/test.war"); - if (!warFile.exists()) + Path warFile = JettyDistribution.resolve("demo-base/webapps/test.war"); + if (!Files.exists(warFile)) { - throw new RuntimeException("Unable to find WAR File: " + warFile.getAbsolutePath()); + throw new FileNotFoundException(warFile.toString()); } - webapp.setWar(warFile.getAbsolutePath()); + webapp.setWarResource(new PathResource(warFile)); webapp.setExtractWAR(true); // This webapp will use jsps and jstl. We need to enable the @@ -89,19 +85,32 @@ public class OneWebAppWithJsp // its own we register it as a bean with the Jetty server object so it // can be started and stopped according to the lifecycle of the server // itself. + String realmResourceName = "etc/realm.properties"; + ClassLoader classLoader = OneWebAppWithJsp.class.getClassLoader(); + URL realmProps = classLoader.getResource(realmResourceName); + if (realmProps == null) + throw new FileNotFoundException("Unable to find " + realmResourceName); + HashLoginService loginService = new HashLoginService(); loginService.setName("Test Realm"); - loginService.setConfig("examples/embedded/src/test/resources/realm.properties"); + loginService.setConfig(realmProps.toExternalForm()); server.addBean(loginService); - // Start things up! + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + + // Start things up! server.start(); server.dumpStdErr(); // The use of server.join() the will make the current thread join and // wait until the server is done executing. - // See http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#join() server.join(); } } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ProxyServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ProxyServer.java index ff3412f6cb3..872c57f102b 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ProxyServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ProxyServer.java @@ -27,11 +27,13 @@ import org.eclipse.jetty.servlet.ServletHolder; public class ProxyServer { - public static void main(String[] args) throws Exception + public static Server createServer(int port) { Server server = new Server(); + + // Establish listening connector ServerConnector connector = new ServerConnector(server); - connector.setPort(8888); + connector.setPort(port); server.addConnector(connector); // Setup proxy handler to handle CONNECT methods @@ -45,6 +47,15 @@ public class ProxyServer proxyServlet.setInitParameter("blackList", "www.eclipse.org"); context.addServlet(proxyServlet, "/*"); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + server.start(); + server.join(); } } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/RewriteServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/RewriteServer.java index a74427f0ffd..0347886b29b 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/RewriteServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/RewriteServer.java @@ -18,27 +18,29 @@ package org.eclipse.jetty.embedded; +import java.util.Arrays; + import org.eclipse.jetty.rewrite.RewriteCustomizer; import org.eclipse.jetty.rewrite.handler.CompactPathRule; import org.eclipse.jetty.rewrite.handler.RewriteRegexRule; -import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; public class RewriteServer { - public static void main(String[] args) throws Exception + public static Server createServer(int port) { - Server server = new Server(8080); - - HttpConfiguration config = server.getConnectors()[0].getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration(); + Server server = new Server(port); RewriteCustomizer rewrite = new RewriteCustomizer(); - config.addCustomizer(rewrite); rewrite.addRule(new CompactPathRule()); rewrite.addRule(new RewriteRegexRule("(.*)foo(.*)", "$1FOO$2")); + Arrays.stream(server.getConnectors()) + .forEach((connector) -> connector.getConnectionFactory(HttpConnectionFactory.class) + .getHttpConfiguration().addCustomizer(rewrite)); + ServletContextHandler context = new ServletContextHandler( ServletContextHandler.SESSIONS); context.setContextPath("/"); @@ -46,6 +48,14 @@ public class RewriteServer context.addServlet(DumpServlet.class, "/*"); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + server.start(); server.join(); } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java index aaa0c806a5e..1f339b33fa2 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java @@ -18,6 +18,8 @@ package org.eclipse.jetty.embedded; +import java.io.FileNotFoundException; +import java.net.URL; import java.util.Collections; import org.eclipse.jetty.security.ConstraintMapping; @@ -30,13 +32,13 @@ import org.eclipse.jetty.util.security.Constraint; public class SecuredHelloHandler { - public static void main(String[] args) throws Exception + public static Server createServer(int port) throws FileNotFoundException { // Create a basic jetty server object that will listen on port 8080. // Note that if you set this to port 0 then a randomly available port // will be assigned that you can either look in the logs for the port, // or programmatically obtain it for use in test cases. - Server server = new Server(8080); + Server server = new Server(port); // Since this example is for our test webapp, we need to setup a // LoginService so this shows how to create a very simple hashmap based @@ -46,8 +48,14 @@ public class SecuredHelloHandler // started and stopped according to the lifecycle of the server itself. // In this example the name can be whatever you like since we are not // dealing with webapp realms. + String realmResourceName = "etc/realm.properties"; + ClassLoader classLoader = SecuredHelloHandler.class.getClassLoader(); + URL realmProps = classLoader.getResource(realmResourceName); + if (realmProps == null) + throw new FileNotFoundException("Unable to find " + realmResourceName); + LoginService loginService = new HashLoginService("MyRealm", - "src/test/resources/realm.properties"); + realmProps.toExternalForm()); server.addBean(loginService); // A security handler is a jetty handler that secures content behind a @@ -68,7 +76,7 @@ public class SecuredHelloHandler constraint.setRoles(new String[]{"user", "admin"}); // Binds a url pattern with the previously created constraint. The roles - // for this constraing mapping are mined from the Constraint itself + // for this constraint mapping are mined from the Constraint itself // although methods exist to declare and bind roles separately as well. ConstraintMapping mapping = new ConstraintMapping(); mapping.setPathSpec("/*"); @@ -92,13 +100,19 @@ public class SecuredHelloHandler // chain the hello handler into the security handler security.setHandler(hh); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + // Start things up! server.start(); // The use of server.join() the will make the current thread join and // wait until the server is done executing. - // See - // http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#join() server.join(); } } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithAnnotations.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithAnnotations.java index 7b148e0c703..869e4214f2a 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithAnnotations.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithAnnotations.java @@ -19,6 +19,9 @@ package org.eclipse.jetty.embedded; import java.io.File; +import java.io.FileNotFoundException; +import java.net.URL; +import javax.naming.NamingException; import org.eclipse.jetty.annotations.AnnotationConfiguration; import org.eclipse.jetty.plus.jndi.EnvEntry; @@ -36,10 +39,10 @@ import org.eclipse.jetty.webapp.WebAppContext; */ public class ServerWithAnnotations { - public static final void main(String[] args) throws Exception + public static Server createServer(int port) throws NamingException, FileNotFoundException { // Create the server - final Server server = new Server(8080); + Server server = new Server(port); // Create a WebApp WebAppContext webapp = new WebAppContext(); @@ -60,7 +63,7 @@ public class ServerWithAnnotations new Transaction(new com.acme.MockUserTransaction()); // Define an env entry with webapp scope. - // THIS ENTRY IS OVERRIDEN BY THE ENTRY IN jetty-env.xml + // THIS ENTRY IS OVERRIDDEN BY THE ENTRY IN jetty-env.xml new EnvEntry(webapp, "maxAmount", 100d, true); // Register a mock DataSource scoped to the webapp @@ -70,10 +73,23 @@ public class ServerWithAnnotations server.addBean(new NamingDump()); // Configure a LoginService + String realmResourceName = "etc/realm.properties"; + ClassLoader classLoader = ServerWithAnnotations.class.getClassLoader(); + URL realmProps = classLoader.getResource(realmResourceName); + if (realmProps == null) + throw new FileNotFoundException("Unable to find " + realmResourceName); + HashLoginService loginService = new HashLoginService(); loginService.setName("Test Realm"); - loginService.setConfig("examples/embedded/src/test/resources/realm.properties"); + loginService.setConfig(realmProps.toExternalForm()); server.addBean(loginService); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.dumpStdErr(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJMX.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJMX.java index 95b38e56a18..c3c14df0b49 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJMX.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJMX.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.embedded; import java.lang.management.ManagementFactory; +import java.net.MalformedURLException; import javax.management.remote.JMXServiceURL; import org.eclipse.jetty.jmx.ConnectorServer; @@ -26,17 +27,16 @@ import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.server.Server; /** - * The simplest possible Jetty server. + * A Jetty Server with JMX enabled for remote connections */ public class ServerWithJMX { - public static void main(String[] args) throws Exception + public static Server createServer(int port) throws MalformedURLException { - // === jetty-jmx.xml === + Server server = new Server(port); + MBeanContainer mbContainer = new MBeanContainer( ManagementFactory.getPlatformMBeanServer()); - - Server server = new Server(8080); server.addBean(mbContainer); ConnectorServer jmx = new ConnectorServer( @@ -48,6 +48,14 @@ public class ServerWithJMX "org.eclipse.jetty.jmx:name=rmiconnectorserver"); server.addBean(jmx); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); + server.start(); server.dumpStdErr(); server.join(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJNDI.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJNDI.java index 456d639007d..6e3b2854bf6 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJNDI.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJNDI.java @@ -18,12 +18,14 @@ package org.eclipse.jetty.embedded; -import java.io.File; +import java.nio.file.Path; import java.util.Properties; +import javax.naming.NamingException; import org.eclipse.jetty.plus.webapp.EnvConfiguration; import org.eclipse.jetty.plus.webapp.PlusConfiguration; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.resource.PathResource; import org.eclipse.jetty.webapp.WebAppContext; /** @@ -31,18 +33,16 @@ import org.eclipse.jetty.webapp.WebAppContext; */ public class ServerWithJNDI { - public static void main(String[] args) throws Exception + public static Server createServer(int port) throws NamingException { - // Create the server - Server server = new Server(8080); + Server server = new Server(port); // Create a WebApp WebAppContext webapp = new WebAppContext(); webapp.setContextPath("/"); - File warFile = new File( - "../../jetty-distribution/target/distribution/demo-base/webapps/test-jndi.war"); - webapp.setWar(warFile.getAbsolutePath()); + Path testJndiWar = JettyDistribution.resolve("demo-base/webapps/test-jndi.war"); + webapp.setWarResource(new PathResource(testJndiWar)); server.setHandler(webapp); // Enable parsing of jndi-related parts of web.xml and jetty-env.xml @@ -74,7 +74,7 @@ public class ServerWithJNDI // Note that the last arg of "true" means that this definition for // "wiggle" would override an entry of the // same name in web.xml - new org.eclipse.jetty.plus.jndi.EnvEntry(webapp, "wiggle", 100D, true); + new org.eclipse.jetty.plus.jndi.EnvEntry(webapp, "wiggle", 100d, true); // Register a reference to a mail service scoped to the webapp. // This must be linked to the webapp by an entry in web.xml: @@ -84,7 +84,8 @@ public class ServerWithJNDI // Container // // At runtime the webapp accesses this as java:comp/env/mail/Session - org.eclipse.jetty.jndi.factories.MailSessionReference mailref = new org.eclipse.jetty.jndi.factories.MailSessionReference(); + org.eclipse.jetty.jndi.factories.MailSessionReference mailref = + new org.eclipse.jetty.jndi.factories.MailSessionReference(); mailref.setUser("CHANGE-ME"); mailref.setPassword("CHANGE-ME"); Properties props = new Properties(); @@ -106,6 +107,13 @@ public class ServerWithJNDI // java:comp/env/jdbc/mydatasource new org.eclipse.jetty.plus.jndi.Resource( webapp, "jdbc/mydatasource", new com.acme.MockDataSource()); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); server.join(); diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SimplestServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SimplestServer.java index a48235c55d5..3bdb6d4fa27 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SimplestServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SimplestServer.java @@ -25,11 +25,20 @@ import org.eclipse.jetty.server.Server; */ public class SimplestServer { + public static Server createServer(int port) + { + Server server = new Server(port); + // This has a connector listening on port specified + // and no handlers, meaning all requests will result + // in a 404 response + return server; + } + public static void main(String[] args) throws Exception { - Server server = new Server(8080); + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); - server.dumpStdErr(); server.join(); } } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SplitFileServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SplitFileServer.java index 4a681baa93e..f8b46272b56 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SplitFileServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SplitFileServer.java @@ -18,16 +18,14 @@ package org.eclipse.jetty.embedded; -import java.io.File; +import java.nio.file.Paths; -import org.eclipse.jetty.server.Connector; -import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.ResourceHandler; -import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.resource.PathResource; import org.eclipse.jetty.util.resource.Resource; /** @@ -37,59 +35,68 @@ import org.eclipse.jetty.util.resource.Resource; */ public class SplitFileServer { - public static void main(String[] args) throws Exception + public static Server createServer(int port, Resource baseResource0, Resource baseResource1) { // Create the Server object and a corresponding ServerConnector and then // set the port for the connector. In this example the server will - // listen on port 8090. If you set this to port 0 then when the server + // listen on port 8080. If you set this to port 0 then when the server // has been started you can called connector.getLocalPort() to // programmatically get the port the server started on. Server server = new Server(); ServerConnector connector = new ServerConnector(server); - connector.setPort(8090); - server.setConnectors(new Connector[]{connector}); + connector.setPort(port); + server.addConnector(connector); // Create a Context Handler and ResourceHandler. The ContextHandler is // getting set to "/" path but this could be anything you like for - // builing out your url. Note how we are setting the ResourceBase using + // building out your url. Note how we are setting the ResourceBase using // our jetty maven testing utilities to get the proper resource // directory, you needn't use these, you simply need to supply the paths // you are looking to serve content from. ResourceHandler rh0 = new ResourceHandler(); + rh0.setDirectoriesListed(false); ContextHandler context0 = new ContextHandler(); context0.setContextPath("/"); - File dir0 = MavenTestingUtils.getTestResourceDir("dir0"); - context0.setBaseResource(Resource.newResource(dir0)); + context0.setBaseResource(baseResource0); context0.setHandler(rh0); // Rinse and repeat the previous item, only specifying a different // resource base. ResourceHandler rh1 = new ResourceHandler(); + rh1.setDirectoriesListed(false); ContextHandler context1 = new ContextHandler(); context1.setContextPath("/"); - File dir1 = MavenTestingUtils.getTestResourceDir("dir1"); - context1.setBaseResource(Resource.newResource(dir1)); + context1.setBaseResource(baseResource1); context1.setHandler(rh1); // Create a ContextHandlerCollection and set the context handlers to it. // This will let jetty process urls against the declared contexts in // order to match up content. - ContextHandlerCollection contexts = new ContextHandlerCollection(); - contexts.setHandlers(new Handler[]{context0, context1}); - + ContextHandlerCollection contexts = new ContextHandlerCollection( + context0, context1 + ); server.setHandler(contexts); + return server; + } - // Start things up! - server.start(); + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Resource resource0 = new PathResource(Paths.get("src/test/resources/dir0")); + Resource resource1 = new PathResource(Paths.get("src/test/resources/dir1")); + + Server server = createServer(port, resource0, resource1); // Dump the server state - System.out.println(server.dump()); + server.setDumpAfterStart(true); + + // Start things up! + server.start(); // The use of server.join() the will make the current thread join and // wait until the server is done executing. - // See http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#join() server.join(); } } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java index 473fae972ab..d8d88c58f05 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java @@ -46,25 +46,37 @@ public class WebSocketJsrServer } } - public static void main(String[] args) throws Exception + public static Server createServer(int port) { - final Server server = new Server(8080); + Server server = new Server(port); HandlerList handlers = new HandlerList(); - ServletContextHandler contextHandler = new ServletContextHandler( - ServletContextHandler.SESSIONS); - contextHandler.setContextPath("/"); - handlers.addHandler(contextHandler); + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/"); + handlers.addHandler(context); + + // Enable javax.websocket configuration for the context + JavaxWebSocketServletContainerInitializer.configure(context, + (servletContext, serverContainer) -> + { + // Add your websocket to the javax.websocket.server.ServerContainer + serverContainer.addEndpoint(EchoJsrSocket.class); + } + ); + handlers.addHandler(new DefaultHandler()); server.setHandler(handlers); - // Enable javax.websocket configuration for the context - JavaxWebSocketServletContainerInitializer.configure(contextHandler, (context, container) -> - container.addEndpoint(EchoJsrSocket.class)); + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); - contextHandler.dumpStdErr(); server.join(); } } diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketServer.java index 40fc6465595..395ec41bce4 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketServer.java @@ -20,13 +20,13 @@ package org.eclipse.jetty.embedded; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.WriteCallback; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.server.JettyWebSocketServlet; import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory; +import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; /** * Example of setting up a Jetty WebSocket server @@ -61,20 +61,29 @@ public class WebSocketServer } } - public static void main(String[] args) throws Exception + public static Server createServer(int port) { - Server server = new Server(8080); + Server server = new Server(port); - ServletContextHandler context = new ServletContextHandler( - ServletContextHandler.SESSIONS); + ServletContextHandler context = new ServletContextHandler(); context.setContextPath("/"); server.setHandler(context); // Add the echo socket servlet to the /echo path map - context.addServlet(new ServletHolder(EchoServlet.class), "/echo"); + context.addServlet(EchoServlet.class, "/echo"); + + // Configure context to support WebSocket + JettyWebSocketServletContainerInitializer.configure(context, null); + + return server; + } + + public static void main(String[] args) throws Exception + { + int port = ExampleUtil.getPort(args, "jetty.http.port", 8080); + Server server = createServer(port); server.start(); - context.dumpStdErr(); server.join(); } } diff --git a/examples/embedded/src/main/resources/content.jar b/examples/embedded/src/main/other/content.jar similarity index 100% rename from examples/embedded/src/main/resources/content.jar rename to examples/embedded/src/main/other/content.jar diff --git a/examples/embedded/src/main/resources/etc/keystore b/examples/embedded/src/main/resources/etc/keystore new file mode 100644 index 00000000000..d6592f95ee9 Binary files /dev/null and b/examples/embedded/src/main/resources/etc/keystore differ diff --git a/examples/embedded/src/main/resources/etc/keystore.pkf b/examples/embedded/src/main/resources/etc/keystore.pkf new file mode 100644 index 00000000000..443818e87df --- /dev/null +++ b/examples/embedded/src/main/resources/etc/keystore.pkf @@ -0,0 +1,20 @@ +Bag Attributes + friendlyName: jetty + localKeyID: 54 69 6D 65 20 31 34 32 33 31 39 38 30 39 33 31 31 35 +Key Attributes: +-----BEGIN PRIVATE KEY----- +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIPh4Q0t4xklXTzX +N2VAb47r5n7idAupp4CTNEhhT6lS70iA+A8i4+0lSEHWAogvd9jl3H7SvScr30QM +4ieC0JCGSOwGc8f+yqKrO56PPd5OuqW380BJ0r74jJczU9CcsuavHD7e6mRLUnmj +xM20NSxrcicMiPUHY1mJZtN9swtxAgMBAAECgYADS9P6Jll0uXBZIu/pgfDH27GJ +HlPULstW9VbrMDNzgfUlFMQebLrRpIrnyleJ29Xc//HA4beEkR4lb0T/w88+pEkt +7fhYeqRLPIfpDOgzloynnsoPcd8f/PypbimQrNLmBiG1178nVcy4Yoh5lYVIJwtU +3VriqDlvAfTLrrx8AQJBAMLWuh27Hb8xs3LRg4UD7hcv8tJejstm08Y+czRz7cO0 +RENa3aDjGFSegc+IUfdez7BP8uDw+PwE+jybmTvaliECQQCtR/anCY1WS28/bKvy +lmIwoI15eraBdVFkN0Hfxh+9PfR3rMD5uyvukT5GgTtY/XxADyafSTaipDJiZHJI +EitRAkBjeCBYYVjUbVlBuvi8Bb+dktsSzzdzXDGtueAy3SR7jyJyiIcxRf775Fg9 +TUkbUwoQ5yAF+sACWcAvBPj796JBAkAEZEeHEkHnxv+pztpIyrDwZJFRW9/WRh/q +90+PGVlilXhltBYr/idt43Z9mPblGX+VrAyhitx8oMa6IauX0gYRAkEAgnyVeXrD +jDLUZRA3P8Gu27k1k6GjbTYiUz3HKCz2/6+MZ2MK2qqwafgqocji029Q6dHdPD7a +4QnRlvraUnyQLA== +-----END PRIVATE KEY----- diff --git a/examples/embedded/src/main/resources/etc/realm.properties b/examples/embedded/src/main/resources/etc/realm.properties new file mode 100644 index 00000000000..f4b3490e910 --- /dev/null +++ b/examples/embedded/src/main/resources/etc/realm.properties @@ -0,0 +1,20 @@ +# +# This file defines users passwords and roles for a HashUserRealm +# +# The format is +# : [, ...] +# +# Passwords may be clear text, obfuscated or checksummed. The class +# org.eclipse.jetty.util.security.Password should be used to generate obfuscated +# passwords or password checksums +# +# If DIGEST Authentication is used, the password must be in a recoverable +# format, either plain text or OBF:. +# +jetty:MD5:164c88b302622e17050af52c89945d44,user +admin:CRYPT:adpexzg3FUZAk,server-administrator,content-administrator,admin,user +other:OBF:1xmk1w261u9r1w1c1xmq,user +plain:plain,user +user:password,user +# This entry is for digest auth. The credential is a MD5 hash of username:realmname:password +digest:MD5:6e120743ad67abfbc385bc2bb754e297,user diff --git a/examples/embedded/src/main/resources/exampleserver.xml b/examples/embedded/src/main/resources/exampleserver.xml index 6dbbd6a07f5..138bdfd53b0 100644 --- a/examples/embedded/src/main/resources/exampleserver.xml +++ b/examples/embedded/src/main/resources/exampleserver.xml @@ -8,7 +8,9 @@ - 8080 + + + diff --git a/examples/embedded/src/main/resources/fileserver.xml b/examples/embedded/src/main/resources/fileserver.xml index 8562cb00071..0f9be210fce 100644 --- a/examples/embedded/src/main/resources/fileserver.xml +++ b/examples/embedded/src/main/resources/fileserver.xml @@ -7,7 +7,9 @@ - 8080 + + + @@ -22,7 +24,9 @@ index.html - . + + + diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/AbstractEmbeddedTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/AbstractEmbeddedTest.java new file mode 100644 index 00000000000..80afde9eafb --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/AbstractEmbeddedTest.java @@ -0,0 +1,64 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; +import org.eclipse.jetty.io.ClientConnector; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +public abstract class AbstractEmbeddedTest +{ + public HttpClient client; + + @BeforeEach + public void startClient() throws Exception + { + SslContextFactory.Client sslContextFactory = new SslContextFactory.Client(); + sslContextFactory.setTrustAll(true); + + ClientConnector clientConnector = new ClientConnector(); + clientConnector.setSelectors(1); + clientConnector.setSslContextFactory(sslContextFactory); + + QueuedThreadPool clientThreads = new QueuedThreadPool(); + clientThreads.setName("client"); + + clientConnector.setExecutor(clientThreads); + + client = new HttpClient(new HttpClientTransportOverHTTP(clientConnector)); + client.start(); + } + + @AfterEach + public void stopClient() throws Exception + { + client.stop(); + } + + protected void dumpResponseHeaders(ContentResponse response) + { + System.out.printf("%s %s %s%n", response.getVersion(), response.getStatus(), response.getReason()); + System.out.println(response.getHeaders()); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerTest.java new file mode 100644 index 00000000000..367b521b28e --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerTest.java @@ -0,0 +1,90 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class ExampleServerTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ExampleServer.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws Exception + { + URI uri = server.getURI().resolve("/hello"); + + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } + + @Test + public void testGetEcho() throws Exception + { + URI uri = server.getURI().resolve("/echo/a/greeting"); + + String postBody = "Greetings from " + ExampleServerTest.class; + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.POST) + .content(new StringContentProvider(postBody)) + .send(); + + // Check the response status code + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString(postBody)); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerXmlTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerXmlTest.java new file mode 100644 index 00000000000..23b28198e05 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ExampleServerXmlTest.java @@ -0,0 +1,67 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class ExampleServerXmlTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ExampleServerXml.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws Exception + { + URI uri = server.getURI().resolve("/hello"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FastFileServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FastFileServerTest.java new file mode 100644 index 00000000000..861ad3c8814 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FastFileServerTest.java @@ -0,0 +1,92 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.io.BufferedWriter; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +@ExtendWith(WorkDirExtension.class) +public class FastFileServerTest extends AbstractEmbeddedTest +{ + private static final String TEXT_CONTENT = "I am an old man and I have known a great " + + "many troubles, but most of them never happened. - Mark Twain"; + public WorkDir workDir; + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + Path baseDir = workDir.getEmptyPathDir(); + + Path textFile = baseDir.resolve("simple.txt"); + try (BufferedWriter writer = Files.newBufferedWriter(textFile, UTF_8)) + { + writer.write(TEXT_CONTENT); + } + + server = FastFileServer.createServer(0, baseDir.toFile()); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetSimpleText() throws Exception + { + URI uri = server.getURI().resolve("/simple.txt"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + HttpFields responseHeaders = response.getHeaders(); + + assertThat("Content-Type", responseHeaders.get("Content-Type"), is("text/plain")); + assertThat("Content-Length", responseHeaders.getLongField("Content-Length"), + is((long)TEXT_CONTENT.length())); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response body", responseBody, is(TEXT_CONTENT)); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerTest.java new file mode 100644 index 00000000000..14ac4e5a53d --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerTest.java @@ -0,0 +1,93 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.io.BufferedWriter; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.resource.PathResource; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +@ExtendWith(WorkDirExtension.class) +public class FileServerTest extends AbstractEmbeddedTest +{ + private static final String TEXT_CONTENT = "I am an old man and I have known a great " + + "many troubles, but most of them never happened. - Mark Twain"; + public WorkDir workDir; + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + Path baseDir = workDir.getEmptyPathDir(); + + Path textFile = baseDir.resolve("simple.txt"); + try (BufferedWriter writer = Files.newBufferedWriter(textFile, UTF_8)) + { + writer.write(TEXT_CONTENT); + } + + server = FileServer.createServer(0, new PathResource(baseDir)); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetSimpleText() throws Exception + { + URI uri = server.getURI().resolve("/simple.txt"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + HttpFields responseHeaders = response.getHeaders(); + + assertThat("Content-Type", responseHeaders.get("Content-Type"), is("text/plain")); + assertThat("Content-Length", responseHeaders.getLongField("Content-Length"), + is((long)TEXT_CONTENT.length())); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response body", responseBody, is(TEXT_CONTENT)); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerXmlTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerXmlTest.java new file mode 100644 index 00000000000..813879e36c4 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/FileServerXmlTest.java @@ -0,0 +1,92 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.io.BufferedWriter; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +@ExtendWith(WorkDirExtension.class) +public class FileServerXmlTest extends AbstractEmbeddedTest +{ + private static final String TEXT_CONTENT = "I am an old man and I have known a great " + + "many troubles, but most of them never happened. - Mark Twain"; + public WorkDir workDir; + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + Path baseDir = workDir.getEmptyPathDir(); + + Path textFile = baseDir.resolve("simple.txt"); + try (BufferedWriter writer = Files.newBufferedWriter(textFile, UTF_8)) + { + writer.write(TEXT_CONTENT); + } + + server = FileServerXml.createServer(0, baseDir); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetSimpleText() throws Exception + { + URI uri = server.getURI().resolve("/simple.txt"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + HttpFields responseHeaders = response.getHeaders(); + + assertThat("Content-Type", responseHeaders.get("Content-Type"), is("text/plain")); + assertThat("Content-Length", responseHeaders.getLongField("Content-Length"), + is((long)TEXT_CONTENT.length())); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response body", responseBody, is(TEXT_CONTENT)); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/JarServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/JarServerTest.java new file mode 100644 index 00000000000..4e4bf368547 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/JarServerTest.java @@ -0,0 +1,83 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class JarServerTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = JarServer.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetDir0Test0() throws Exception + { + URI uri = server.getURI().resolve("/dir0/test0.txt"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("test0")); + } + + @Test + public void testGetDir1Test1() throws Exception + { + URI uri = server.getURI().resolve("/dir1/test1.txt"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("test1")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/LikeJettyXmlTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/LikeJettyXmlTest.java new file mode 100644 index 00000000000..445ebe9d4db --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/LikeJettyXmlTest.java @@ -0,0 +1,97 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; +import java.util.Map; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +public class LikeJettyXmlTest extends AbstractEmbeddedTest +{ + private Server server; + private URI serverPlainUri; + private URI serverSslUri; + + @BeforeEach + public void startServer() throws Exception + { + assumeTrue(JettyDistribution.DISTRIBUTION != null, "jetty-distribution not found"); + + server = LikeJettyXml.createServer(0, 0, false); + server.start(); + + Map ports = ServerUtil.fixDynamicPortConfigurations(server); + + // Establish base URI's that use "localhost" to prevent tripping over + // the "REMOTE ACCESS" warnings in demo-base + serverPlainUri = URI.create("http://localhost:" + ports.get("plain") + "/"); + serverSslUri = URI.create("https://localhost:" + ports.get("secure") + "/"); + } + + @AfterEach + public void stopServer() throws Exception + { + LifeCycle.stop(server); + } + + @Test + public void testGetTest() throws Exception + { + URI uri = serverPlainUri.resolve("/test/"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } + + @Test + public void testGetTestSsl() throws Exception + { + URI uri = serverSslUri.resolve("/test/"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyConnectorsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyConnectorsTest.java new file mode 100644 index 00000000000..0a19dac2859 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyConnectorsTest.java @@ -0,0 +1,95 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; +import java.util.Map; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class ManyConnectorsTest extends AbstractEmbeddedTest +{ + private Server server; + private URI serverPlainUri; + private URI serverSslUri; + + @BeforeEach + public void startServer() throws Exception + { + server = ManyConnectors.createServer(0, 0); + server.start(); + + Map ports = ServerUtil.fixDynamicPortConfigurations(server); + + // Establish base URI's that use "localhost" to prevent tripping over + // the "REMOTE ACCESS" warnings in demo-base + serverPlainUri = URI.create("http://localhost:" + ports.get("plain") + "/"); + serverSslUri = URI.create("https://localhost:" + ports.get("secure") + "/"); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testPlainGetHello() throws Exception + { + URI uri = serverPlainUri.resolve("/hello"); + + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } + + @Test + public void testSecureGetHello() throws Exception + { + URI uri = serverSslUri.resolve("/hello"); + + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyContextsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyContextsTest.java new file mode 100644 index 00000000000..bd439aee552 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyContextsTest.java @@ -0,0 +1,117 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class ManyContextsTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ManyContexts.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetRootHello() throws Exception + { + URI uri = server.getURI().resolve("/"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Root Hello")); + } + + @Test + public void testGetFrenchHello() throws Exception + { + URI uri = server.getURI().resolve("/fr"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Bonjour")); + } + + @Test + public void testGetItalianGoodMorning() throws Exception + { + URI uri = server.getURI().resolve("/it"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Buongiorno")); + } + + @Test + public void testGetVirtualHostHello() throws Exception + { + int port = server.getURI().getPort(); + + URI uri = URI.create("http://127.0.0.2:" + port + "/"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Virtual Hello")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyHandlersTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyHandlersTest.java new file mode 100644 index 00000000000..a6641d7c3d7 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyHandlersTest.java @@ -0,0 +1,105 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; +import java.util.Map; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.ajax.JSON; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class ManyHandlersTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ManyHandlers.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetParams() throws Exception + { + URI uri = server.getURI().resolve("/params?a=b&foo=bar"); + + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .header(HttpHeader.ACCEPT_ENCODING, "gzip") + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test gzip + // Test that Gzip was used to produce the response + String contentEncoding = response.getHeaders().get(HttpHeader.CONTENT_ENCODING); + assertThat("Content-Encoding", contentEncoding, containsString("gzip")); + + // test response content + String responseBody = response.getContentAsString(); + Object jsonObj = JSON.parse(responseBody); + Map jsonMap = (Map)jsonObj; + assertThat("Response JSON keys.size", jsonMap.keySet().size(), is(2)); + } + + @Test + public void testGetHello() throws Exception + { + URI uri = server.getURI().resolve("/hello"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .header(HttpHeader.ACCEPT_ENCODING, "gzip") + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test gzip + // Test that Gzip was used to produce the response + String contentEncoding = response.getHeaders().get(HttpHeader.CONTENT_ENCODING); + assertThat("Content-Encoding", contentEncoding, containsString("gzip")); + + // test expected header from wrapper + String welcome = response.getHeaders().get("X-Welcome"); + assertThat("X-Welcome header", welcome, containsString("Greetings from WelcomeWrapHandler")); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyServletContextsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyServletContextsTest.java new file mode 100644 index 00000000000..6cc21115957 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ManyServletContextsTest.java @@ -0,0 +1,115 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class ManyServletContextsTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ManyServletContexts.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws Exception + { + URI uri = server.getURI().resolve("/hello"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } + + @Test + public void testGetItalianHello() throws Exception + { + URI uri = server.getURI().resolve("/it/hello"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Ciao")); + } + + @Test + public void testGetFrenchHello() throws Exception + { + URI uri = server.getURI().resolve("/fr/hello"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Bonjour")); + } + + @Test + public void testGetOtherYo() throws Exception + { + URI uri = server.getURI().resolve("/other/hello.yo"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("YO!")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/MinimalServletsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/MinimalServletsTest.java new file mode 100644 index 00000000000..18354a1f07c --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/MinimalServletsTest.java @@ -0,0 +1,67 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class MinimalServletsTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = MinimalServlets.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws Exception + { + URI uri = server.getURI().resolve("/hello"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneConnectorTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneConnectorTest.java new file mode 100644 index 00000000000..d700bb0272b --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneConnectorTest.java @@ -0,0 +1,67 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class OneConnectorTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = OneConnector.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws Exception + { + URI uri = server.getURI().resolve("/hello"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneContextTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneContextTest.java new file mode 100644 index 00000000000..9d12b7f1a36 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneContextTest.java @@ -0,0 +1,67 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class OneContextTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = OneContext.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws Exception + { + URI uri = server.getURI().resolve("/hello"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneHandlerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneHandlerTest.java new file mode 100644 index 00000000000..920e24c59f4 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneHandlerTest.java @@ -0,0 +1,67 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class OneHandlerTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = OneHandler.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws Exception + { + URI uri = server.getURI().resolve("/hello"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextJmxStatsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextJmxStatsTest.java new file mode 100644 index 00000000000..ae627ca6c9c --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextJmxStatsTest.java @@ -0,0 +1,101 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; +import java.util.Set; +import javax.management.MBeanServer; +import javax.management.ObjectInstance; +import javax.management.ObjectName; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.io.ConnectionStatistics; +import org.eclipse.jetty.jmx.MBeanContainer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.opentest4j.AssertionFailedError; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + +@ExtendWith(WorkDirExtension.class) +public class OneServletContextJmxStatsTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = OneServletContextJmxStats.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetDumpViaPathInfo() throws Exception + { + URI uri = server.getURI().resolve("/dump/something"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, + allOf( + containsString("DumpServlet"), + containsString("servletPath=/dump"), + containsString("pathInfo=/something") + ) + ); + } + + @Test + public void testJmxConnectStatsPresent() throws Exception + { + MBeanContainer mbeanContainer = server.getBean(MBeanContainer.class); + MBeanServer mbeanServer = mbeanContainer.getMBeanServer(); + + String domain = ConnectionStatistics.class.getPackage().getName(); + Set mbeanNames = mbeanServer.queryNames(ObjectName.getInstance(domain + ":type=connectionstatistics,*"), null); + ObjectName connStatsName = mbeanNames.stream().findFirst().orElseThrow(AssertionFailedError::new); + ObjectInstance mbeanConnStats = mbeanServer.getObjectInstance(connStatsName); + Number connections = (Number)mbeanServer.getAttribute(connStatsName, "connections"); + assertThat("stats[connections]", connections, is(notNullValue())); + assertThat("stats[connections]", connections.longValue(), greaterThanOrEqualTo(0L)); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextTest.java new file mode 100644 index 00000000000..5ed2433a912 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextTest.java @@ -0,0 +1,158 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.io.BufferedWriter; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.resource.PathResource; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +@ExtendWith(WorkDirExtension.class) +public class OneServletContextTest extends AbstractEmbeddedTest +{ + private static final String TEXT_CONTENT = "The secret of getting ahead is getting started. - Mark Twain"; + public WorkDir workDir; + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + Path baseDir = workDir.getEmptyPathDir(); + + Path textFile = baseDir.resolve("simple.txt"); + try (BufferedWriter writer = Files.newBufferedWriter(textFile, UTF_8)) + { + writer.write(TEXT_CONTENT); + } + + server = OneServletContext.createServer(0, new PathResource(baseDir)); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws Exception + { + URI uri = server.getURI().resolve("/hello/there"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hello")); + } + + @Test + public void testGetDumpViaPathInfo() throws Exception + { + URI uri = server.getURI().resolve("/dump/something"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, + allOf( + containsString("DumpServlet"), + containsString("servletPath=/dump"), + containsString("pathInfo=/something") + ) + ); + } + + @Test + public void testGetDumpSuffix() throws Exception + { + URI uri = server.getURI().resolve("/another.dump"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, + allOf( + containsString("DumpServlet"), + containsString("servletPath=/another.dump"), + containsString("pathInfo=null") + ) + ); + } + + @Test + public void testGetTestDumpSuffix() throws Exception + { + URI uri = server.getURI().resolve("/test/another.dump"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + String filterResponseHeader = response.getHeaders().get("X-TestFilter"); + assertThat("X-TestFilter header", filterResponseHeader, is("true")); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, + allOf( + containsString("DumpServlet"), + containsString("servletPath=/test/another.dump"), + containsString("pathInfo=null"), + containsString("request.attribute[X-ReqListener]=true"), + containsString("servletContext.attribute[X-Init]=true") + ) + ); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextWithSessionTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextWithSessionTest.java new file mode 100644 index 00000000000..0b96d4dedbc --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneServletContextWithSessionTest.java @@ -0,0 +1,95 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.io.BufferedWriter; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; +import org.eclipse.jetty.util.resource.PathResource; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +@ExtendWith(WorkDirExtension.class) +public class OneServletContextWithSessionTest extends AbstractEmbeddedTest +{ + private static final String TEXT_CONTENT = "Do the right thing. It will gratify some people and astonish the rest. - Mark Twain"; + public WorkDir workDir; + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + Path baseDir = workDir.getEmptyPathDir(); + + Path textFile = baseDir.resolve("simple.txt"); + try (BufferedWriter writer = Files.newBufferedWriter(textFile, UTF_8)) + { + writer.write(TEXT_CONTENT); + } + + server = OneServletContextWithSession.createServer(0, new PathResource(baseDir)); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetHello() throws Exception + { + URI uri = server.getURI().resolve("/"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + String setCookieValue = response.getHeaders().get(HttpHeader.SET_COOKIE); + assertThat("Set-Cookie value", setCookieValue, containsString("JSESSIONID=")); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, + allOf( + containsString("session.getId() = "), + containsString("session.isNew() = true") + ) + ); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppTest.java new file mode 100644 index 00000000000..7f9eae15003 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppTest.java @@ -0,0 +1,71 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +public class OneWebAppTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + assumeTrue(JettyDistribution.DISTRIBUTION != null, "jetty-distribution not found"); + + server = OneWebApp.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + LifeCycle.stop(server); + } + + @Test + public void testGetAsyncRest() throws Exception + { + URI uri = server.getURI().resolve("/testAsync?items=mouse,beer,gnome"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Asynchronous: mouse,beer,gnome")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppWithJspTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppWithJspTest.java new file mode 100644 index 00000000000..9f858925596 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/OneWebAppWithJspTest.java @@ -0,0 +1,112 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +public class OneWebAppWithJspTest extends AbstractEmbeddedTest +{ + private Server server; + private URI serverLocalUri; + + @BeforeEach + public void startServer() throws Exception + { + assumeTrue(JettyDistribution.DISTRIBUTION != null, "jetty-distribution not found"); + + server = OneWebAppWithJsp.createServer(0); + server.start(); + + // Use URI based on "localhost" to get past "REMOTE ACCESS!" protection of demo war + serverLocalUri = URI.create("http://localhost:" + server.getURI().getPort() + "/"); + } + + @AfterEach + public void stopServer() throws Exception + { + LifeCycle.stop(server); + } + + @Test + public void testGetDumpInfo() throws Exception + { + URI uri = serverLocalUri.resolve("/dump/info"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("getProtocol: HTTP/1.1")); + } + + @Test + public void testGetJspExpr() throws Exception + { + URI uri = serverLocalUri.resolve("/jsp/expr.jsp?A=1"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + String userAgent = client.getUserAgentField().getValue(); + assertThat("Response Content", responseBody, containsString("" + userAgent + "")); + } + + @Test + public void testGetJstlExpr() throws Exception + { + URI uri = serverLocalUri.resolve("/jsp/jstl.jsp"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("

JSTL Example

")); + for (int i = 1; i <= 10; i++) + { + assertThat("Reponse content (counting)", responseBody, containsString("" + i)); + } + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ProxyServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ProxyServerTest.java new file mode 100644 index 00000000000..c53d8a96740 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ProxyServerTest.java @@ -0,0 +1,72 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; + +import org.eclipse.jetty.client.HttpProxy; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class ProxyServerTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ProxyServer.createServer(0); + server.start(); + + URI uri = server.getURI(); + client.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", uri.getPort())); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetProxiedRFC() throws Exception + { + URI uri = URI.create("https://tools.ietf.org/rfc/rfc7230.txt"); + + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/RewriteServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/RewriteServerTest.java new file mode 100644 index 00000000000..775f550208c --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/RewriteServerTest.java @@ -0,0 +1,83 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class RewriteServerTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = RewriteServer.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetRewriteFooInName() throws Exception + { + URI uri = server.getURI().resolve("/do-be-foo-be-do"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("requestURI=/do-be-FOO-be-do")); + } + + @Test + public void testGetRewriteFooInPath() throws Exception + { + URI uri = server.getURI().resolve("/do/be/foo/be/do.it"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("requestURI=/do/be/FOO/be/do.it")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SecuredHelloHandlerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SecuredHelloHandlerTest.java new file mode 100644 index 00000000000..892330b392c --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SecuredHelloHandlerTest.java @@ -0,0 +1,85 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; +import java.util.Base64; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class SecuredHelloHandlerTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = SecuredHelloHandler.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetWithoutAuth() throws Exception + { + URI uri = server.getURI().resolve("/hello"); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.UNAUTHORIZED_401)); + + // dumpResponseHeaders(response); + } + + @Test + public void testGetWithAuth() throws Exception + { + URI uri = server.getURI().resolve("/hello"); + + String authEncoded = Base64.getEncoder().encodeToString("user:password".getBytes(UTF_8)); + ContentResponse response = client.newRequest(uri) + .method(HttpMethod.GET) + .header(HttpHeader.AUTHORIZATION, "Basic " + authEncoded) + .send(); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("

Hello World

")); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerUtil.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerUtil.java new file mode 100644 index 00000000000..ad41662e45c --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerUtil.java @@ -0,0 +1,88 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; + +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class ServerUtil +{ + /** + * Fix the HttpConfiguration entries for securePort after the dynamic ports have been bound. + * + * @param server the server to correct. + */ + public static Map fixDynamicPortConfigurations(Server server) + { + // Fix ports in HttpConfiguration (since we are using dynamic port assignment for this testcase) + HttpConfiguration plainHttpConfiguration = null; + HttpConfiguration secureHttpConfiguration = null; + int plainHttpPort = -1; + int secureHttpPort = -1; + + for (Connector connector : server.getConnectors()) + { + if (connector instanceof ServerConnector) + { + ServerConnector serverConnector = (ServerConnector)connector; + SslConnectionFactory sslConnectionFactory = serverConnector.getConnectionFactory(SslConnectionFactory.class); + HttpConnectionFactory httpConnectionFactory = serverConnector.getConnectionFactory(HttpConnectionFactory.class); + if (httpConnectionFactory != null) + { + HttpConfiguration configuration = httpConnectionFactory.getHttpConfiguration(); + if (sslConnectionFactory != null) + { + secureHttpConfiguration = configuration; + secureHttpPort = serverConnector.getLocalPort(); + } + else + { + plainHttpConfiguration = configuration; + plainHttpPort = serverConnector.getLocalPort(); + } + } + } + } + + assertNotNull(plainHttpConfiguration, "Plain HTTP Configuration"); + assertNotEquals(plainHttpPort, -1, "Dynamic Plain HTTP Port"); + + assertNotNull(secureHttpConfiguration, "Secure HTTP Configuration"); + assertNotEquals(secureHttpPort, -1, "Dynamic Secure Port"); + + plainHttpConfiguration.setSecurePort(secureHttpPort); + secureHttpConfiguration.setSecurePort(secureHttpPort); + + Map ports = new HashMap<>(); + ports.put("plain", plainHttpPort); + ports.put("secure", secureHttpPort); + + return ports; + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithAnnotationsTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithAnnotationsTest.java new file mode 100644 index 00000000000..3662334ea26 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithAnnotationsTest.java @@ -0,0 +1,70 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +public class ServerWithAnnotationsTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + assumeTrue(JettyDistribution.DISTRIBUTION != null, "jetty-distribution not found"); + + server = ServerWithAnnotations.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + LifeCycle.stop(server); + } + + @Test + public void testGetTest() throws Exception + { + URI uri = server.getURI().resolve("/test"); + ContentResponse response = client.GET(uri); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("maxAmount=55.0")); + assertThat("Response Content", responseBody, not(containsString(""))); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJMXTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJMXTest.java new file mode 100644 index 00000000000..9a5d3ab9d2a --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJMXTest.java @@ -0,0 +1,62 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.util.Optional; +import java.util.Set; +import javax.management.MBeanServer; +import javax.management.ObjectName; + +import org.eclipse.jetty.jmx.MBeanContainer; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ServerWithJMXTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = ServerWithJMX.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetTest() throws Exception + { + MBeanContainer mbeanContainer = server.getBean(MBeanContainer.class); + MBeanServer mbeanServer = mbeanContainer.getMBeanServer(); + + String name = "org.eclipse.jetty.jmx:name=rmiconnectorserver,*"; + Set mbeanNames = mbeanServer.queryNames(ObjectName.getInstance(name), null); + Optional rmiConnectorNameOptional = mbeanNames.stream().findFirst(); + assertTrue(rmiConnectorNameOptional.isPresent(), "Has RMI Connector Server"); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJNDITest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJNDITest.java new file mode 100644 index 00000000000..603b4ae8023 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/ServerWithJNDITest.java @@ -0,0 +1,78 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +public class ServerWithJNDITest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + assumeTrue(JettyDistribution.DISTRIBUTION != null, "jetty-distribution not found"); + + server = ServerWithJNDI.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + LifeCycle.stop(server); + } + + @Test + public void testGetTest() throws Exception + { + URI uri = server.getURI().resolve("/test"); + ContentResponse response = client.GET(uri); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, + allOf( + containsString("java:comp/env/woggle"), + containsString("java:comp/env/gargle"), + containsString("java:comp/env/wiggle") + ) + ); + + assertThat("Response Content", responseBody, not(containsString(""))); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SimplestServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SimplestServerTest.java new file mode 100644 index 00000000000..a21a8e438e6 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SimplestServerTest.java @@ -0,0 +1,57 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class SimplestServerTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = SimplestServer.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetTest() throws Exception + { + URI uri = server.getURI().resolve("/test"); + ContentResponse response = client.GET(uri); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.NOT_FOUND_404)); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SplitFileServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SplitFileServerTest.java new file mode 100644 index 00000000000..5e5411260fc --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/SplitFileServerTest.java @@ -0,0 +1,95 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.resource.PathResource; +import org.eclipse.jetty.util.resource.Resource; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.is; + +public class SplitFileServerTest extends AbstractEmbeddedTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + Path path0 = Paths.get("src/test/resources/dir0"); + Path path1 = Paths.get("src/test/resources/dir1"); + Resource resource0 = new PathResource(path0); + Resource resource1 = new PathResource(path1); + + server = SplitFileServer.createServer(0, resource0, resource1); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetTest0() throws Exception + { + URI uri = server.getURI().resolve("/test0.txt"); + ContentResponse response = client.GET(uri); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("test0")); + } + + @Test + public void testGetTest1() throws Exception + { + URI uri = server.getURI().resolve("/test1.txt"); + ContentResponse response = client.GET(uri); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.OK_200)); + + // dumpResponseHeaders(response); + + // test response content + String responseBody = response.getContentAsString(); + assertThat("Response Content", responseBody, containsString("test1")); + } + + @Test + public void testGetTest2() throws Exception + { + URI uri = server.getURI().resolve("/test2.txt"); + ContentResponse response = client.GET(uri); + assertThat("HTTP Response Status", response.getStatus(), is(HttpStatus.NOT_FOUND_404)); + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/TestXml.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/TestXml.java deleted file mode 100644 index bb45cf6c124..00000000000 --- a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/TestXml.java +++ /dev/null @@ -1,31 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -package org.eclipse.jetty.embedded; - -import org.eclipse.jetty.xml.XmlConfiguration; - -public class TestXml -{ - public static void main(String[] args) throws Exception - { - System.setProperty("jetty.home", "../jetty-distribution/target/distribution"); - XmlConfiguration.main("../jetty-jmx/src/main/config/etc/jetty-jmx.xml", - "../jetty-server/src/main/config/etc/jetty.xml"); - } -} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/WebSocketJsrServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/WebSocketJsrServerTest.java new file mode 100644 index 00000000000..34a74d8a24a --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/WebSocketJsrServerTest.java @@ -0,0 +1,110 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; +import java.util.concurrent.LinkedBlockingQueue; +import javax.websocket.CloseReason; +import javax.websocket.ContainerProvider; +import javax.websocket.Endpoint; +import javax.websocket.EndpointConfig; +import javax.websocket.MessageHandler; +import javax.websocket.Session; +import javax.websocket.WebSocketContainer; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.websocket.api.util.WSURI; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class WebSocketJsrServerTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = WebSocketJsrServer.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetEcho() throws Exception + { + WebSocketContainer javaxWebSocketClient = ContainerProvider.getWebSocketContainer(); + javaxWebSocketClient.setDefaultMaxSessionIdleTimeout(2000); + try + { + URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/echo")); + + TrackingClientEndpoint clientEndpoint = new TrackingClientEndpoint(); + + Session session = javaxWebSocketClient.connectToServer(clientEndpoint, wsUri); + session.getBasicRemote().sendText("Hello World"); + + String response = clientEndpoint.messages.poll(2, SECONDS); + assertThat("Response", response, is("Hello World")); + } + finally + { + LifeCycle.stop(javaxWebSocketClient); + } + } + + public static class TrackingClientEndpoint extends Endpoint implements MessageHandler.Whole + { + public LinkedBlockingQueue messages = new LinkedBlockingQueue<>(); + + @Override + public void onMessage(String message) + { + messages.offer(message); + } + + @Override + public void onOpen(Session session, EndpointConfig config) + { + session.addMessageHandler(this); + } + + @Override + public void onError(Session session, Throwable thr) + { + super.onError(session, thr); + } + + @Override + public void onClose(Session session, CloseReason closeReason) + { + super.onClose(session, closeReason); + } + } +} diff --git a/examples/embedded/src/test/java/org/eclipse/jetty/embedded/WebSocketServerTest.java b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/WebSocketServerTest.java new file mode 100644 index 00000000000..26f2e42d899 --- /dev/null +++ b/examples/embedded/src/test/java/org/eclipse/jetty/embedded/WebSocketServerTest.java @@ -0,0 +1,111 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.embedded; + +import java.net.URI; +import java.time.Duration; +import java.util.concurrent.Future; +import java.util.concurrent.LinkedBlockingQueue; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError; +import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; +import org.eclipse.jetty.websocket.api.annotations.WebSocket; +import org.eclipse.jetty.websocket.api.util.WSURI; +import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +public class WebSocketServerTest +{ + private Server server; + + @BeforeEach + public void startServer() throws Exception + { + server = WebSocketServer.createServer(0); + server.start(); + } + + @AfterEach + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testGetEcho() throws Exception + { + WebSocketClient webSocketClient = new WebSocketClient(); + webSocketClient.setIdleTimeout(Duration.ofSeconds(2)); + try + { + webSocketClient.start(); + URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/echo")); + + TrackingClientEndpoint clientEndpoint = new TrackingClientEndpoint(); + + Future sessionFut = webSocketClient.connect(clientEndpoint, wsUri); + Session session = sessionFut.get(2, SECONDS); + session.getRemote().sendString("Hello World"); + + String response = clientEndpoint.messages.poll(2, SECONDS); + assertThat("Response", response, is("Hello World")); + } + finally + { + LifeCycle.stop(webSocketClient); + } + } + + @WebSocket + public static class TrackingClientEndpoint + { + private static final Logger LOG = Log.getLogger(TrackingClientEndpoint.class); + public LinkedBlockingQueue messages = new LinkedBlockingQueue<>(); + + @OnWebSocketMessage + public void onMessage(String message) + { + messages.offer(message); + } + + @OnWebSocketError + public void onError(Throwable cause) + { + LOG.warn(cause); + } + + @OnWebSocketClose + public void onClose(int statusCode, String reason) + { + LOG.debug("Closed({}, {})", statusCode, reason); + } + } +} diff --git a/examples/embedded/src/test/resources/jetty-logging.properties b/examples/embedded/src/test/resources/jetty-logging.properties new file mode 100644 index 00000000000..b17c80c3330 --- /dev/null +++ b/examples/embedded/src/test/resources/jetty-logging.properties @@ -0,0 +1,11 @@ +org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog +org.eclipse.jetty.LEVEL=WARN +org.eclipse.jetty.embedded.JettyDistribution.LEVEL=DEBUG +#org.eclipse.jetty.STACKS=true +#org.eclipse.jetty.STACKS=false +#org.eclipse.jetty.io.LEVEL=DEBUG +#org.eclipse.jetty.io.ssl.LEVEL=DEBUG +#org.eclipse.jetty.server.LEVEL=DEBUG +#org.eclipse.jetty.servlets.LEVEL=DEBUG +#org.eclipse.jetty.alpn.LEVEL=DEBUG +#org.eclipse.jetty.jmx.LEVEL=DEBUG diff --git a/jetty-deploy/src/main/config/etc/jetty-deploy.xml b/jetty-deploy/src/main/config/etc/jetty-deploy.xml index c279a388389..d244576c146 100644 --- a/jetty-deploy/src/main/config/etc/jetty-deploy.xml +++ b/jetty-deploy/src/main/config/etc/jetty-deploy.xml @@ -36,9 +36,9 @@ --> - + - + jetty.deploy.monitoredPath diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java index 64de4a6444e..5e53d5f8930 100644 --- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java +++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java @@ -172,7 +172,7 @@ public class DeploymentManager extends ContainerLifeCycle for (AppProvider provider : providers) { if (_providers.add(provider)) - addBean(provider); + addBean(provider, true); } } @@ -186,7 +186,7 @@ public class DeploymentManager extends ContainerLifeCycle if (isRunning()) throw new IllegalStateException(); _providers.add(provider); - addBean(provider); + addBean(provider, true); } public void setLifeCycleBindings(Collection bindings) @@ -523,6 +523,7 @@ public class DeploymentManager extends ContainerLifeCycle catch (Throwable t) { LOG.warn("Unable to reach node goal: " + nodeName, t); + // migrate to FAILED node Node failed = _lifecycle.getNodeByName(AppLifeCycle.FAILED); appentry.setLifeCycleNode(failed); diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/PropertiesConfigurationManager.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/PropertiesConfigurationManager.java index e7708db5b08..4bd3c538afb 100644 --- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/PropertiesConfigurationManager.java +++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/PropertiesConfigurationManager.java @@ -20,7 +20,6 @@ package org.eclipse.jetty.deploy; import java.io.FileNotFoundException; import java.io.IOException; -import java.net.MalformedURLException; import java.util.HashMap; import java.util.Map; import java.util.Properties; @@ -29,6 +28,7 @@ import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedOperation; import org.eclipse.jetty.util.annotation.Name; +import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.resource.Resource; /** @@ -37,21 +37,33 @@ import org.eclipse.jetty.util.resource.Resource; * Supplies properties defined in a file. */ @ManagedObject("Configure deployed webapps via properties") -public class PropertiesConfigurationManager implements ConfigurationManager +public class PropertiesConfigurationManager implements ConfigurationManager, Dumpable { private String _properties; - private final Map _map = new HashMap(); + private final Map _map = new HashMap<>(); public PropertiesConfigurationManager(String properties) { + if (properties != null) + { + try + { + setFile(properties); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } } public PropertiesConfigurationManager() { + this(null); } @ManagedAttribute("A file or URL of properties") - public void setFile(String resource) throws MalformedURLException, IOException + public void setFile(String resource) throws IOException { _properties = resource; _map.clear(); @@ -75,7 +87,7 @@ public class PropertiesConfigurationManager implements ConfigurationManager @Override public Map getProperties() { - return new HashMap<>(_map); + return _map; } private void loadProperties(String resource) throws FileNotFoundException, IOException @@ -86,9 +98,25 @@ public class PropertiesConfigurationManager implements ConfigurationManager Properties properties = new Properties(); properties.load(file.getInputStream()); for (Map.Entry entry : properties.entrySet()) - { _map.put(entry.getKey().toString(), String.valueOf(entry.getValue())); - } } } + + @Override + public String toString() + { + return String.format("%s@%x{%s}", this.getClass(), hashCode(), _properties); + } + + @Override + public String dump() + { + return Dumpable.dump(this); + } + + @Override + public void dump(Appendable out, String indent) throws IOException + { + Dumpable.dumpObjects(out, indent, toString(), _map); + } } diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBinding.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBinding.java index 17f8353a26a..b57afd183c1 100644 --- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBinding.java +++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/GlobalWebappConfigBinding.java @@ -20,7 +20,9 @@ package org.eclipse.jetty.deploy.bindings; import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppLifeCycle; +import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.deploy.graph.Node; +import org.eclipse.jetty.deploy.providers.WebAppProvider; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -63,7 +65,7 @@ public class GlobalWebappConfigBinding implements AppLifeCycle.Binding { return new String[]{"deploying"}; } - + @Override public void processBinding(Node node, App app) throws Exception { @@ -94,6 +96,13 @@ public class GlobalWebappConfigBinding implements AppLifeCycle.Binding XmlConfiguration jettyXmlConfig = new XmlConfiguration(globalContextSettings); Resource resource = Resource.newResource(app.getOriginId()); app.getDeploymentManager().scope(jettyXmlConfig, resource); + AppProvider appProvider = app.getAppProvider(); + if (appProvider instanceof WebAppProvider) + { + WebAppProvider webAppProvider = ((WebAppProvider)appProvider); + if (webAppProvider.getConfigurationManager() != null) + jettyXmlConfig.getProperties().putAll(webAppProvider.getConfigurationManager().getProperties()); + } WebAppClassLoader.runWithServerClassAccess(() -> { jettyXmlConfig.configure(context); diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java index 0507816c76b..dae686bf0ea 100644 --- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java +++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ScanningAppProvider.java @@ -36,7 +36,7 @@ import org.eclipse.jetty.util.Scanner; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedOperation; -import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; @@ -45,7 +45,7 @@ import org.eclipse.jetty.util.resource.Resource; * */ @ManagedObject("Abstract Provider for loading webapps") -public abstract class ScanningAppProvider extends AbstractLifeCycle implements AppProvider +public abstract class ScanningAppProvider extends ContainerLifeCycle implements AppProvider { private static final Logger LOG = Log.getLogger(ScanningAppProvider.class); @@ -81,11 +81,13 @@ public abstract class ScanningAppProvider extends AbstractLifeCycle implements A protected ScanningAppProvider() { + this(null); } protected ScanningAppProvider(FilenameFilter filter) { _filenameFilter = filter; + addBean(_appMap); } protected void setFilenameFilter(FilenameFilter filter) @@ -142,15 +144,19 @@ public abstract class ScanningAppProvider extends AbstractLifeCycle implements A _scanner.setFilenameFilter(_filenameFilter); _scanner.setReportDirs(true); _scanner.addListener(_scannerListener); - _scanner.start(); + + addBean(_scanner); + + super.doStart(); } @Override protected void doStop() throws Exception { + super.doStop(); if (_scanner != null) { - _scanner.stop(); + removeBean(_scanner); _scanner.removeListener(_scannerListener); _scanner = null; } @@ -307,4 +313,10 @@ public abstract class ScanningAppProvider extends AbstractLifeCycle implements A ); _scanner.scan(); } + + @Override + public String toString() + { + return String.format("%s@%x%s", this.getClass(), hashCode(), _monitored); + } } diff --git a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java index 5217e862ce9..8c26d9e4da8 100644 --- a/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java +++ b/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/WebAppProvider.java @@ -206,6 +206,7 @@ public class WebAppProvider extends ScanningAppProvider */ public void setConfigurationManager(ConfigurationManager configurationManager) { + updateBean(_configurationManager, configurationManager); _configurationManager = configurationManager; } diff --git a/jetty-documentation/src/main/asciidoc/distribution-guide/sessions/session-configuration-sessioncache.adoc b/jetty-documentation/src/main/asciidoc/distribution-guide/sessions/session-configuration-sessioncache.adoc index d06c0f74f28..cd7ebfa5a0e 100644 --- a/jetty-documentation/src/main/asciidoc/distribution-guide/sessions/session-configuration-sessioncache.adoc +++ b/jetty-documentation/src/main/asciidoc/distribution-guide/sessions/session-configuration-sessioncache.adoc @@ -34,6 +34,7 @@ Once the `session-cache-hash` module has been enabled, you can view a list of al #jetty.session.saveOnInactiveEvict=false #jetty.session.saveOnCreate=false #jetty.session.removeUnloadableSessions=false +#jetty.session.flushOnResponseCommit=false ---- jetty.session.evictionPolicy:: @@ -63,6 +64,12 @@ jetty.session.removeUnloadableSessions:: Boolean, default `false`. Controls whether a session that cannot be restored - for example because it is corrupted - from the `SessionDataStore` is deleted by the `SessionDataStore`. +jetty.session.flushOnResponseCommit:: +Boolean, default `false`. +If true, if a session is "dirty" - ie its attributes have changed - it will be written to the backing store as the response is about to commit. +This ensures that all subsequent requests whether to the same or different node will see the updated session data. +If false, a dirty session will only be written to the backing store when the last simultaneous request for it leaves the session. + For more general information on the uses of these configuration properties, see link:#sessions-details[Session Components]. @@ -72,4 +79,21 @@ The `NullSessionCache` is a trivial implementation of the `SessionCache` that do You may need to use it if your clustering setup does not have a sticky load balancer, or if you want absolutely minimal support for sessions. If you use this in conjunction with the `NullSessionDataStore`, then sessions will neither be retained in memory nor persisted. -To enable the `NullSessionCache`, enable the `sesssion-cache-null` link:#startup-modules[module]. +To enable the `NullSessionCache`, enable the `sesssion-cache-null` link:#startup-modules[module]. +Configuration options are: + +jetty.session.saveOnCreate:: +Boolean, default `false`. +Controls whether a session that is newly created will be immediately saved to the `SessionDataStore` or lazily saved as the last request for the session exits. + +jetty.session.removeUnloadableSessions:: +Boolean, default `false`. +Controls whether a session that cannot be restored - for example because it is corrupted - from the `SessionDataStore` is deleted by the `SessionDataStore`. + +jetty.session.flushOnResponseCommit:: +Boolean, default `false`. +If true, if a session is "dirty" - ie its attributes have changed - it will be written to the backing store as the response is about to commit. +This ensures that all subsequent requests whether to the same or different node will see the updated session data. +If false, a dirty session will only be written to the backing store when the last simultaneous request for it leaves the session. + +For more general information on the uses of these configuration properties, see link:#sessions-details[Session Components]. diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java b/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java index aea328cca0a..e9c4e1606c9 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java @@ -20,9 +20,14 @@ package org.eclipse.jetty.http.pathmap; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; public class ServletPathSpec extends PathSpec { + + private static final Logger LOG = Log.getLogger(ServletPathSpec.class); + /** * If a servlet or filter path mapping isn't a suffix mapping, ensure * it starts with '/' @@ -213,13 +218,13 @@ public class ServletPathSpec extends PathSpec super.pathDepth = 0; char lastChar = servletPathSpec.charAt(specLength - 1); // prefix based - if ((servletPathSpec.charAt(0) == '/') && (specLength > 1) && (lastChar == '*')) + if (servletPathSpec.charAt(0) == '/' && servletPathSpec.endsWith("/*")) { this.group = PathSpecGroup.PREFIX_GLOB; this.prefix = servletPathSpec.substring(0, specLength - 2); } // suffix based - else if (servletPathSpec.charAt(0) == '*') + else if (servletPathSpec.charAt(0) == '*' && servletPathSpec.length() > 1) { this.group = PathSpecGroup.SUFFIX_GLOB; this.suffix = servletPathSpec.substring(2, specLength); @@ -228,6 +233,11 @@ public class ServletPathSpec extends PathSpec { this.group = PathSpecGroup.EXACT; this.prefix = servletPathSpec; + if (servletPathSpec.endsWith("*") ) + { + LOG.warn("Suspicious URL pattern: '{}'; see sections 12.1 and 12.2 of the Servlet specification", + servletPathSpec); + } } for (int i = 0; i < specLength; i++) @@ -276,11 +286,6 @@ public class ServletPathSpec extends PathSpec { throw new IllegalArgumentException("Servlet Spec 12.2 violation: glob '*' can only exist at end of prefix based matches: bad spec \"" + servletPathSpec + "\""); } - - if (idx < 1 || servletPathSpec.charAt(idx - 1) != '/') - { - throw new IllegalArgumentException("Servlet Spec 12.2 violation: suffix glob '*' can only exist after '/': bad spec \"" + servletPathSpec + "\""); - } } else if (servletPathSpec.startsWith("*.")) { diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/pathmap/PathMappingsTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/pathmap/PathMappingsTest.java index 018dc430f43..a609f55d8ba 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/pathmap/PathMappingsTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/pathmap/PathMappingsTest.java @@ -231,6 +231,8 @@ public class PathMappingsTest assertTrue(!new ServletPathSpec("/foo/*").matches("/bar/anything"), "!match /foo/*"); assertTrue(new ServletPathSpec("*.foo").matches("anything.foo"), "match *.foo"); assertTrue(!new ServletPathSpec("*.foo").matches("anything.bar"), "!match *.foo"); + assertTrue(new ServletPathSpec("/On*").matches("/On*"), "match /On*"); + assertTrue(!new ServletPathSpec("/On*").matches("/One"), "!match /One"); assertEquals("10", p.getMatch("/").getResource(), "match / with ''"); @@ -287,7 +289,6 @@ public class PathMappingsTest @ValueSource(strings = { "*", "/foo/*/bar", - "/foo*", "*/foo", "*.foo/*" }) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java index da76d773ed9..36057b2c9cb 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ManagedSelector.java @@ -43,6 +43,7 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.component.DumpableCollection; @@ -122,12 +123,20 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable start._started.await(); } + protected void onSelectFailed(Throwable cause) + { + // override to change behavior + } + public int size() { Selector s = _selector; if (s == null) return 0; - return s.keys().size(); + Set keys = s.keys(); + if (keys == null) + return 0; + return keys.size(); } @Override @@ -135,7 +144,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable { // doStop might be called for a failed managedSelector, // We do not want to wait twice, so we only stop once for each start - if (_started.compareAndSet(true, false)) + if (_started.compareAndSet(true, false) && _selector != null) { // Close connections, but only wait a single selector cycle for it to take effect CloseConnections closeConnections = new CloseConnections(); @@ -210,7 +219,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable catch (RejectedExecutionException x) { if (task instanceof Closeable) - closeNoExceptions((Closeable)task); + IO.close((Closeable)task); } } @@ -246,17 +255,14 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable } } - private static void closeNoExceptions(Closeable closeable) + protected void endPointOpened(EndPoint endPoint) { - try - { - if (closeable != null) - closeable.close(); - } - catch (Throwable x) - { - LOG.ignore(x); - } + _selectorManager.endPointOpened(endPoint); + } + + protected void endPointClosed(EndPoint endPoint) + { + _selectorManager.endPointClosed(endPoint); } private void createEndPoint(SelectableChannel channel, SelectionKey selectionKey) throws IOException @@ -266,7 +272,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable endPoint.setConnection(connection); selectionKey.attach(endPoint); endPoint.onOpen(); - _selectorManager.endPointOpened(endPoint); + endPointOpened(endPoint); _selectorManager.connectionOpened(connection); if (LOG.isDebugEnabled()) LOG.debug("Created {}", endPoint); @@ -496,15 +502,19 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable } catch (Throwable x) { + IO.close(_selector); _selector = null; + if (isRunning()) - LOG.warn(x); + { + LOG.warn("Fatal select() failure", x); + onSelectFailed(x); + } else { LOG.warn(x.toString()); LOG.debug(x); } - closeNoExceptions(_selector); } return false; } @@ -541,13 +551,13 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable { LOG.debug("Ignoring cancelled key for channel {}", key.channel()); if (attachment instanceof EndPoint) - closeNoExceptions((EndPoint)attachment); + IO.close((EndPoint)attachment); } catch (Throwable x) { LOG.warn("Could not process key for channel " + key.channel(), x); if (attachment instanceof EndPoint) - closeNoExceptions((EndPoint)attachment); + IO.close((EndPoint)attachment); } } else @@ -556,7 +566,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable LOG.debug("Selector loop ignoring invalid key for channel {}", key.channel()); Object attachment = key.attachment(); if (attachment instanceof EndPoint) - closeNoExceptions((EndPoint)attachment); + IO.close((EndPoint)attachment); } } return null; @@ -661,7 +671,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable } catch (Throwable x) { - closeNoExceptions(_channel); + IO.close(_channel); LOG.warn(x); } } @@ -683,7 +693,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable } catch (Throwable x) { - closeNoExceptions(channel); + IO.close(channel); LOG.warn("Accept failed for channel " + channel, x); } @@ -722,7 +732,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable public void close() { LOG.debug("closed accept of {}", channel); - closeNoExceptions(channel); + IO.close(channel); } @Override @@ -735,7 +745,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable } catch (Throwable x) { - closeNoExceptions(channel); + IO.close(channel); _selectorManager.onAcceptFailed(channel, x); LOG.debug(x); } @@ -758,7 +768,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable protected void failed(Throwable failure) { - closeNoExceptions(channel); + IO.close(channel); LOG.warn(String.valueOf(failure)); LOG.debug(failure); _selectorManager.onAcceptFailed(channel, failure); @@ -808,7 +818,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable if (failed.compareAndSet(false, true)) { timeout.cancel(); - closeNoExceptions(channel); + IO.close(channel); ManagedSelector.this._selectorManager.connectionFailed(channel, failure, attachment); } } @@ -864,12 +874,12 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable { if (_closed == null) { - closeNoExceptions(closeable); + IO.close(closeable); } else if (!_closed.contains(closeable)) { _closed.add(closeable); - closeNoExceptions(closeable); + IO.close(closeable); } } } @@ -894,12 +904,12 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable { Object attachment = key.attachment(); if (attachment instanceof EndPoint) - closeNoExceptions((EndPoint)attachment); + IO.close((EndPoint)attachment); } } _selector = null; - closeNoExceptions(selector); + IO.close(selector); _stopped.countDown(); } } @@ -924,7 +934,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable } catch (Throwable failure) { - closeNoExceptions(_connect.channel); + IO.close(_connect.channel); LOG.warn(String.valueOf(failure)); LOG.debug(failure); _connect.failed(failure); @@ -957,7 +967,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable Connection connection = endPoint.getConnection(); if (connection != null) _selectorManager.connectionClosed(connection, cause); - _selectorManager.endPointClosed(endPoint); + ManagedSelector.this.endPointClosed(endPoint); } @Override diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyEffectiveWebXml.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyEffectiveWebXml.java index 9749cb16c04..ca2f83df9aa 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyEffectiveWebXml.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyEffectiveWebXml.java @@ -28,8 +28,9 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.eclipse.jetty.annotations.AnnotationConfiguration; +import org.eclipse.jetty.quickstart.QuickStartConfiguration; +import org.eclipse.jetty.quickstart.QuickStartConfiguration.Mode; import org.eclipse.jetty.util.IO; -import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.thread.QueuedThreadPool; @@ -92,30 +93,23 @@ public class JettyEffectiveWebXml extends JettyRunMojo configureWebApplication(); //set the webapp up to do very little other than generate the quickstart-web.xml + if (effectiveWebXml == null) + { + deleteOnExit = true; + effectiveWebXml = new File(target, "effective-web.xml"); + effectiveWebXml.deleteOnExit(); + } + Resource descriptor = Resource.newResource(effectiveWebXml); + if (!effectiveWebXml.getParentFile().exists()) + effectiveWebXml.getParentFile().mkdirs(); + if (!effectiveWebXml.exists()) + effectiveWebXml.createNewFile(); + webApp.setCopyWebDir(false); webApp.setCopyWebInf(false); - webApp.setGenerateQuickStart(true); - - //if the user didn't nominate a file to generate into, pick the name and - //make sure that it is deleted on exit - if (webApp.getQuickStartWebDescriptor() == null) - { - if (effectiveWebXml == null) - { - deleteOnExit = true; - effectiveWebXml = new File(target, "effective-web.xml"); - effectiveWebXml.deleteOnExit(); - } - - Resource descriptor = Resource.newResource(effectiveWebXml); - - if (!effectiveWebXml.getParentFile().exists()) - effectiveWebXml.getParentFile().mkdirs(); - if (!effectiveWebXml.exists()) - effectiveWebXml.createNewFile(); - - webApp.setQuickStartWebDescriptor(descriptor); - } + webApp.addConfiguration(new QuickStartConfiguration()); + webApp.setAttribute(QuickStartConfiguration.MODE, Mode.GENERATE); + webApp.setAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML, descriptor); ServerSupport.addWebApplication(server, webApp); @@ -158,7 +152,7 @@ public class JettyEffectiveWebXml extends JettyRunMojo try { //just show the result in the log - getLog().info(IO.toString(webApp.getQuickStartWebDescriptor().getInputStream())); + getLog().info(IO.toString(((Resource)webApp.getAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML)).getInputStream())); } catch (Exception e) { diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java index cd255b6962c..feada7f24e0 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunForkedMojo.java @@ -43,6 +43,8 @@ import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.eclipse.jetty.annotations.AnnotationConfiguration; +import org.eclipse.jetty.quickstart.QuickStartConfiguration; +import org.eclipse.jetty.quickstart.QuickStartConfiguration.Mode; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.resource.Resource; @@ -209,22 +211,19 @@ public class JettyRunForkedMojo extends JettyRunMojo configureWebApplication(); //set the webapp up to do very little other than generate the quickstart-web.xml + if (forkWebXml == null) + forkWebXml = new File(target, "fork-web.xml"); + + if (!forkWebXml.getParentFile().exists()) + forkWebXml.getParentFile().mkdirs(); + if (!forkWebXml.exists()) + forkWebXml.createNewFile(); + + webApp.addConfiguration(new MavenQuickStartConfiguration()); + webApp.setAttribute(QuickStartConfiguration.MODE, Mode.GENERATE); + webApp.setAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML, Resource.newResource(forkWebXml)); webApp.setCopyWebDir(false); - webApp.setCopyWebInf(false); - webApp.setGenerateQuickStart(true); - - if (webApp.getQuickStartWebDescriptor() == null) - { - if (forkWebXml == null) - forkWebXml = new File(target, "fork-web.xml"); - - if (!forkWebXml.getParentFile().exists()) - forkWebXml.getParentFile().mkdirs(); - if (!forkWebXml.exists()) - forkWebXml.createNewFile(); - - webApp.setQuickStartWebDescriptor(Resource.newResource(forkWebXml)); - } + webApp.setCopyWebInf(false); //add webapp to our fake server instance ServerSupport.addWebApplication(server, webApp); @@ -240,11 +239,10 @@ public class JettyRunForkedMojo extends JettyRunMojo //leave everything unpacked for the forked process to use webApp.setPersistTempDirectory(true); + File props = null; webApp.start(); //just enough to generate the quickstart - //save config of the webapp BEFORE we stop - final File props = prepareConfiguration(); - + props = prepareConfiguration(); webApp.stop(); if (tpool != null) @@ -311,7 +309,7 @@ public class JettyRunForkedMojo extends JettyRunMojo if (PluginLog.getLog().isDebugEnabled()) PluginLog.getLog().debug("Forked cli:" + Arrays.toString(cmd.toArray())); - + PluginLog.getLog().info("Forked process starting"); //set up extra environment vars if there are any diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java index 79c8509ad70..8e765504b1e 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java @@ -34,7 +34,6 @@ import org.eclipse.jetty.annotations.AnnotationConfiguration; import org.eclipse.jetty.plus.webapp.EnvConfiguration; import org.eclipse.jetty.plus.webapp.PlusConfiguration; import org.eclipse.jetty.quickstart.QuickStartConfiguration; -import org.eclipse.jetty.quickstart.QuickStartConfiguration.Mode; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.FilterMapping; import org.eclipse.jetty.servlet.ServletHolder; @@ -105,8 +104,6 @@ public class JettyWebAppContext extends WebAppContext */ private boolean _baseAppFirst = true; - private boolean _isGenerateQuickStart; - public JettyWebAppContext() throws Exception { super(); @@ -117,6 +114,8 @@ public class JettyWebAppContext extends WebAppContext addConfiguration(new EnvConfiguration()); addConfiguration(new PlusConfiguration()); addConfiguration(new AnnotationConfiguration()); + + setAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE, "origin"); } public void setContainerIncludeJarPattern(String pattern) @@ -210,27 +209,6 @@ public class JettyWebAppContext extends WebAppContext return attr == null ? null : attr.toString(); } - /** - * Toggle whether or not the origin attribute will be generated into the - * xml. - * - * @param generateOrigin if true then the origin of each xml element is - * added, otherwise it is omitted. - */ - public void setGenerateOrigin(boolean generateOrigin) - { - setAttribute(QuickStartConfiguration.GENERATE_ORIGIN, generateOrigin); - } - - /** - * @return true if the origin attribute will be generated, false otherwise - */ - public boolean isGenerateOrigin() - { - Object attr = getAttribute(QuickStartConfiguration.GENERATE_ORIGIN); - return attr == null ? false : Boolean.valueOf(attr.toString()); - } - public List getOverlays() { return _overlays; @@ -246,35 +224,6 @@ public class JettyWebAppContext extends WebAppContext return _baseAppFirst; } - /** - * Set the file to use into which to generate the quickstart output. - * - * @param quickStartWebXml the full path to the file to use - */ - public void setQuickStartWebDescriptor(String quickStartWebXml) throws Exception - { - setQuickStartWebDescriptor(Resource.newResource(quickStartWebXml)); - } - - /** - * Set the Resource to use into which to generate the quickstart output. - */ - protected void setQuickStartWebDescriptor(Resource quickStartWebXml) - { - setAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML, quickStartWebXml.toString()); - } - - public Resource getQuickStartWebDescriptor() throws Exception - { - Object o = getAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML); - if (o == null) - return null; - else if (o instanceof Resource) - return (Resource)o; - else - return Resource.newResource((String)o); - } - /** * This method is provided as a convenience for jetty maven plugin * configuration @@ -307,41 +256,9 @@ public class JettyWebAppContext extends WebAppContext return _webInfClasses; } - /** - * If true, a quickstart for the webapp is generated. - * - * @param quickStart if true the quickstart is generated, false otherwise - */ - public void setGenerateQuickStart(boolean quickStart) - { - _isGenerateQuickStart = quickStart; - } - - public boolean isGenerateQuickStart() - { - return _isGenerateQuickStart; - } - @Override public void doStart() throws Exception { - - // choose if this will be a quickstart or normal start - if (!isGenerateQuickStart() && getQuickStartWebDescriptor() != null) - { - MavenQuickStartConfiguration quickStart = new MavenQuickStartConfiguration(); - quickStart.setMode(Mode.QUICKSTART); - quickStart.setQuickStartWebXml(getQuickStartWebDescriptor()); - addConfiguration(quickStart); - } - else if (isGenerateQuickStart()) - { - MavenQuickStartConfiguration quickStart = new MavenQuickStartConfiguration(); - quickStart.setMode(Mode.GENERATE); - quickStart.setQuickStartWebXml(getQuickStartWebDescriptor()); - addConfiguration(quickStart); - } - // Set up the pattern that tells us where the jars are that need // scanning diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenQuickStartConfiguration.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenQuickStartConfiguration.java index 597c313555e..63d643a7a24 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenQuickStartConfiguration.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/MavenQuickStartConfiguration.java @@ -18,15 +18,12 @@ package org.eclipse.jetty.maven.plugin; -import java.io.File; - import org.eclipse.jetty.quickstart.QuickStartConfiguration; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceCollection; -import org.eclipse.jetty.webapp.WebAppClassLoader; import org.eclipse.jetty.webapp.WebAppContext; /** @@ -36,56 +33,6 @@ public class MavenQuickStartConfiguration extends QuickStartConfiguration { private static final Logger LOG = Log.getLogger(QuickStartConfiguration.class); - private Resource _quickStartWebXml; //the descriptor to use for starting/generating quickstart - - public void setQuickStartWebXml(Resource quickStartWebXml) - { - _quickStartWebXml = quickStartWebXml; - } - - @Override - public Resource getQuickStartWebXml(WebAppContext context) throws Exception - { - if (_quickStartWebXml == null) - return super.getQuickStartWebXml(context); - - return _quickStartWebXml; - } - - @Override - public void preConfigure(WebAppContext context) throws Exception - { - //check that webapp is suitable for quick start - if (context.getBaseResource() == null) - throw new IllegalStateException("No location for webapp"); - - //look for quickstart-web.xml in WEB-INF of webapp - Resource quickStartWebXml = getQuickStartWebXml(context); - if (LOG.isDebugEnabled()) - LOG.debug("quickStartWebXml={}", quickStartWebXml); - super.preConfigure(context); - } - - @Override - public void configure(WebAppContext context) throws Exception - { - JettyWebAppContext jwac = (JettyWebAppContext)context; - - //put the classes dir and all dependencies into the classpath - if (jwac.getClassPathFiles() != null) - { - if (LOG.isDebugEnabled()) - LOG.debug("Setting up classpath ..."); - for (File classPathFile : jwac.getClassPathFiles()) - { - ((WebAppClassLoader)context.getClassLoader()).addClassPath(classPathFile.getCanonicalPath()); - } - } - - //Set up the quickstart environment for the context - super.configure(context); - } - @Override public void deconfigure(WebAppContext context) throws Exception { diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java index 7d76d8124a5..9c25a5c8328 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/Starter.java @@ -24,6 +24,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.eclipse.jetty.quickstart.QuickStartConfiguration; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ShutdownMonitor; @@ -71,14 +72,9 @@ public class Starter //configure webapp from properties file describing unassembled webapp configureWebApp(); - - //make it a quickstart if the quickstart-web.xml file exists - if (webApp.getTempDirectory() != null) - { - File qs = new File(webApp.getTempDirectory(), "quickstart-web.xml"); - if (qs.exists() && qs.isFile()) - webApp.setQuickStartWebDescriptor(Resource.newResource(qs)); - } + + webApp.addConfiguration(new QuickStartConfiguration()); + webApp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART); ServerSupport.addWebApplication(server, webApp); @@ -232,7 +228,9 @@ public class Starter public static final void main(String[] args) { if (args == null) + { System.exit(1); + } Starter starter = null; try diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/WebAppPropertyConverter.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/WebAppPropertyConverter.java index d5ca0c745e6..fed62949ce8 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/WebAppPropertyConverter.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/WebAppPropertyConverter.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import java.util.Properties; +import org.eclipse.jetty.quickstart.QuickStartConfiguration; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.resource.Resource; @@ -71,9 +72,10 @@ public class WebAppPropertyConverter props.put("web.xml", webApp.getDescriptor()); } - if (webApp.getQuickStartWebDescriptor() != null) + Object tmp = webApp.getAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML); + if (tmp != null) { - props.put("quickstart.web.xml", webApp.getQuickStartWebDescriptor().getFile().getAbsolutePath()); + props.put("quickstart.web.xml", tmp.toString()); } //sort out the context path @@ -183,11 +185,10 @@ public class WebAppPropertyConverter if (!StringUtil.isBlank(str)) webApp.setDescriptor(str); - //TODO the WebAppStarter class doesn't set up the QUICKSTART_CONFIGURATION_CLASSES, but the Starter class does!!! str = props.getProperty("quickstart.web.xml"); if (!StringUtil.isBlank(str)) { - webApp.setQuickStartWebDescriptor(Resource.newResource(new File(str))); + webApp.setAttribute(QuickStartConfiguration.QUICKSTART_WEB_XML, Resource.newResource(str)); } // - the tmp directory diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncMiddleManServletTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncMiddleManServletTest.java index c64d64fb571..a9dbb178f84 100644 --- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncMiddleManServletTest.java +++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AsyncMiddleManServletTest.java @@ -78,6 +78,7 @@ import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.StacklessLogging; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.OS; @@ -88,6 +89,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +@Disabled("See issue #3974") public class AsyncMiddleManServletTest { private static final Logger LOG = Log.getLogger(AsyncMiddleManServletTest.class); diff --git a/jetty-quickstart/src/main/config/etc/example-quickstart.xml b/jetty-quickstart/src/main/config/etc/example-quickstart.xml deleted file mode 100644 index c042d89d724..00000000000 --- a/jetty-quickstart/src/main/config/etc/example-quickstart.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - true - / - /application.war - - diff --git a/jetty-quickstart/src/main/config/etc/jetty-quickstart.xml b/jetty-quickstart/src/main/config/etc/jetty-quickstart.xml new file mode 100644 index 00000000000..84d030260b0 --- /dev/null +++ b/jetty-quickstart/src/main/config/etc/jetty-quickstart.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + /etc/quickstart-webapp.xml + + + + + + diff --git a/jetty-quickstart/src/main/config/modules/jetty-quickstart.d/quickstart-webapp.xml b/jetty-quickstart/src/main/config/modules/jetty-quickstart.d/quickstart-webapp.xml new file mode 100644 index 00000000000..b0f07545e95 --- /dev/null +++ b/jetty-quickstart/src/main/config/modules/jetty-quickstart.d/quickstart-webapp.xml @@ -0,0 +1,28 @@ + + + + + + org.eclipse.jetty.quickstart.origin + + + + + org.eclipse.jetty.quickstart.xml + + + + + org.eclipse.jetty.quickstart.mode + + + + + + + + true + false + false + + diff --git a/jetty-quickstart/src/main/config/modules/quickstart.mod b/jetty-quickstart/src/main/config/modules/quickstart.mod index 102801714b6..c531ea648d0 100644 --- a/jetty-quickstart/src/main/config/modules/quickstart.mod +++ b/jetty-quickstart/src/main/config/modules/quickstart.mod @@ -6,8 +6,21 @@ deployment of preconfigured webapplications. [depend] server -plus -annotations +deploy [lib] lib/jetty-quickstart-${jetty.version}.jar + +[xml] +etc/jetty-quickstart.xml + +[files] +basehome:modules/jetty-quickstart.d/quickstart-webapp.xml|etc/quickstart-webapp.xml + + +[ini-template] + +# Modes are AUTO, GENERATE, QUICKSTART +# jetty.quickstart.mode=AUTO +# jetty.quickstart.origin=origin +# jetty.quickstart.xml= diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.java index 627f676588a..92814695c33 100644 --- a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.java +++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/PreconfigureQuickStartWar.java @@ -20,11 +20,15 @@ package org.eclipse.jetty.quickstart; import java.util.Locale; +import org.eclipse.jetty.annotations.AnnotationConfiguration; +import org.eclipse.jetty.plus.webapp.EnvConfiguration; +import org.eclipse.jetty.plus.webapp.PlusConfiguration; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.JarResource; import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.xml.XmlConfiguration; public class PreconfigureQuickStartWar @@ -98,7 +102,13 @@ public class PreconfigureQuickStartWar final Server server = new Server(); - QuickStartWebApp webapp = new QuickStartWebApp(); + WebAppContext webapp = new WebAppContext(); + webapp.addConfiguration(new QuickStartConfiguration(), + new EnvConfiguration(), + new PlusConfiguration(), + new AnnotationConfiguration()); + webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.GENERATE); + webapp.setAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE, ""); if (xml != null) { @@ -108,10 +118,21 @@ public class PreconfigureQuickStartWar xmlConfiguration.configure(webapp); } webapp.setResourceBase(dir.getFile().getAbsolutePath()); - webapp.setMode(QuickStartConfiguration.Mode.GENERATE); server.setHandler(webapp); - server.start(); - server.stop(); + try + { + server.setDryRun(true); + server.start(); + } + catch (Exception e) + { + throw e; + } + finally + { + if (!server.isStopped()) + server.stop(); + } } private static void error(String message) diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java index 2462ad4e326..2dc417e42d8 100644 --- a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java +++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartConfiguration.java @@ -18,6 +18,7 @@ package org.eclipse.jetty.quickstart; +import java.io.File; import java.io.IOException; import java.util.HashSet; import java.util.Set; @@ -26,6 +27,8 @@ import java.util.stream.Collectors; import org.eclipse.jetty.annotations.AnnotationConfiguration; import org.eclipse.jetty.annotations.AnnotationDecorator; import org.eclipse.jetty.annotations.ServletContainerInitializersStarter; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; @@ -39,17 +42,16 @@ import org.eclipse.jetty.webapp.WebXmlConfiguration; /** * QuickStartConfiguration *

- * Re-inflate a deployable webapp from a saved effective-web.xml - * which combines all pre-parsed web xml descriptors and annotations. + * Prepare for quickstart generation, or usage. */ public class QuickStartConfiguration extends AbstractConfiguration { private static final Logger LOG = Log.getLogger(QuickStartConfiguration.class); public static final Set> __replacedConfigurations = new HashSet<>(); - public static final String ORIGIN_ATTRIBUTE = "org.eclipse.jetty.quickstart.ORIGIN_ATTRIBUTE"; - public static final String GENERATE_ORIGIN = "org.eclipse.jetty.quickstart.GENERATE_ORIGIN"; - public static final String QUICKSTART_WEB_XML = "org.eclipse.jetty.quickstart.QUICKSTART_WEB_XML"; + public static final String ORIGIN_ATTRIBUTE = "org.eclipse.jetty.quickstart.origin"; + public static final String QUICKSTART_WEB_XML = "org.eclipse.jetty.quickstart.xml"; + public static final String MODE = "org.eclipse.jetty.quickstart.mode"; static { @@ -59,41 +61,35 @@ public class QuickStartConfiguration extends AbstractConfiguration __replacedConfigurations.add(org.eclipse.jetty.annotations.AnnotationConfiguration.class); } - ; + /** Configure the server for the quickstart mode. + *

In practise this means calling server.setDryRun(true) for GENERATE mode

+ * @see Server#setDryRun(boolean) + * @param server The server to configure + * @param mode The quickstart mode + */ + public static void configureMode(Server server, String mode) + { + if (mode != null && Mode.valueOf(mode) == Mode.GENERATE) + server.setDryRun(true); + } public enum Mode { - DISABLED, // No Quick start GENERATE, // Generate quickstart-web.xml and then stop AUTO, // use or generate depending on the existance of quickstart-web.xml QUICKSTART // Use quickstart-web.xml } - ; - private Mode _mode = Mode.AUTO; private boolean _quickStart; public QuickStartConfiguration() { - super(true); + super(false); addDependencies(WebInfConfiguration.class); addDependents(WebXmlConfiguration.class); } - public void setMode(Mode mode) - { - _mode = mode; - } - - public Mode getMode() - { - return _mode; - } - - /** - * @see org.eclipse.jetty.webapp.AbstractConfiguration#preConfigure(org.eclipse.jetty.webapp.WebAppContext) - */ @Override public void preConfigure(WebAppContext context) throws Exception { @@ -106,39 +102,46 @@ public class QuickStartConfiguration extends AbstractConfiguration Resource quickStartWebXml = getQuickStartWebXml(context); LOG.debug("quickStartWebXml={} exists={}", quickStartWebXml, quickStartWebXml.exists()); + //Get the mode + Mode mode = (Mode)context.getAttribute(MODE); + if (mode != null) + _mode = mode; + _quickStart = false; + switch (_mode) { - case DISABLED: - super.preConfigure(context); - break; - case GENERATE: { + if (quickStartWebXml.exists()) + LOG.info("Regenerating {}", quickStartWebXml); + else + LOG.info("Generating {}", quickStartWebXml); + super.preConfigure(context); + //generate the quickstart file then abort QuickStartGeneratorConfiguration generator = new QuickStartGeneratorConfiguration(true); configure(generator, context); context.addConfiguration(generator); break; } - case AUTO: { if (quickStartWebXml.exists()) - quickStart(context, quickStartWebXml); + { + quickStart(context); + } else { + if (LOG.isDebugEnabled()) + LOG.debug("No quickstart xml file, starting webapp {} normally", context); super.preConfigure(context); - QuickStartGeneratorConfiguration generator = new QuickStartGeneratorConfiguration(false); - configure(generator, context); - context.addConfiguration(generator); } break; } - case QUICKSTART: if (quickStartWebXml.exists()) - quickStart(context, quickStartWebXml); + quickStart(context); else throw new IllegalStateException("No " + quickStartWebXml); break; @@ -151,27 +154,20 @@ public class QuickStartConfiguration extends AbstractConfiguration protected void configure(QuickStartGeneratorConfiguration generator, WebAppContext context) throws IOException { Object attr; - attr = context.getAttribute(GENERATE_ORIGIN); - if (attr != null) - generator.setGenerateOrigin(Boolean.valueOf(attr.toString())); attr = context.getAttribute(ORIGIN_ATTRIBUTE); if (attr != null) generator.setOriginAttribute(attr.toString()); - attr = context.getAttribute(QUICKSTART_WEB_XML); - if (attr instanceof Resource) - generator.setQuickStartWebXml((Resource)attr); - else if (attr != null) - generator.setQuickStartWebXml(Resource.newResource(attr.toString())); + + generator.setQuickStartWebXml((Resource)context.getAttribute(QUICKSTART_WEB_XML)); } - /** - * @see org.eclipse.jetty.webapp.AbstractConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext) - */ @Override public void configure(WebAppContext context) throws Exception { if (!_quickStart) + { super.configure(context); + } else { //add the processor to handle normal web.xml content @@ -195,14 +191,27 @@ public class QuickStartConfiguration extends AbstractConfiguration } } - protected void quickStart(WebAppContext context, Resource quickStartWebXml) + @Override + public void postConfigure(WebAppContext context) throws Exception + { + super.postConfigure(context); + ServletContainerInitializersStarter starter = (ServletContainerInitializersStarter)context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZER_STARTER); + if (starter != null) + { + context.removeBean(starter); + context.removeAttribute(AnnotationConfiguration.CONTAINER_INITIALIZER_STARTER); + } + } + + protected void quickStart(WebAppContext context) throws Exception { + LOG.info("Quickstarting {}", context); _quickStart = true; context.setConfigurations(context.getWebAppConfigurations().stream() .filter(c -> !__replacedConfigurations.contains(c.replaces()) && !__replacedConfigurations.contains(c.getClass())) .collect(Collectors.toList()).toArray(new Configuration[]{})); - context.getMetaData().setWebXml(quickStartWebXml); + context.getMetaData().setWebXml((Resource)context.getAttribute(QUICKSTART_WEB_XML)); context.getServletContext().setEffectiveMajorVersion(context.getMetaData().getWebXml().getMajorVersion()); context.getServletContext().setEffectiveMinorVersion(context.getMetaData().getWebXml().getMinorVersion()); } @@ -216,12 +225,38 @@ public class QuickStartConfiguration extends AbstractConfiguration */ public Resource getQuickStartWebXml(WebAppContext context) throws Exception { + Object attr = context.getAttribute(QUICKSTART_WEB_XML); + if (attr instanceof Resource) + return (Resource)attr; + Resource webInf = context.getWebInf(); if (webInf == null || !webInf.exists()) - throw new IllegalStateException("No WEB-INF"); - LOG.debug("webinf={}", webInf); + { + File tmp = new File(context.getBaseResource().getFile(), "WEB-INF"); + tmp.mkdirs(); + webInf = context.getWebInf(); + } - Resource quickStartWebXml = webInf.addPath("quickstart-web.xml"); - return quickStartWebXml; + Resource qstart; + if (attr == null || StringUtil.isBlank(attr.toString())) + { + qstart = webInf.addPath("quickstart-web.xml"); + } + else + { + try + { + // Try a relative resolution + qstart = Resource.newResource(webInf.getFile().toPath().resolve(attr.toString())); + } + catch (Throwable th) + { + // try as a resource + qstart = (Resource.newResource(attr.toString())); + } + context.setAttribute(QUICKSTART_WEB_XML, qstart); + } + context.setAttribute(QUICKSTART_WEB_XML, qstart); + return qstart; } } diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartGeneratorConfiguration.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartGeneratorConfiguration.java index 20cfab4535b..753f32dce0d 100644 --- a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartGeneratorConfiguration.java +++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartGeneratorConfiguration.java @@ -52,6 +52,7 @@ import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletMapping; import org.eclipse.jetty.util.QuotedStringTokenizer; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; @@ -61,10 +62,11 @@ import org.eclipse.jetty.webapp.MetaData; import org.eclipse.jetty.webapp.MetaData.OriginInfo; import org.eclipse.jetty.webapp.MetaInfConfiguration; import org.eclipse.jetty.webapp.WebAppContext; +import org.eclipse.jetty.webapp.WebInfConfiguration; import org.eclipse.jetty.xml.XmlAppendable; /** - * QuickStartDescriptorGenerator + * QuickStartGeneratorConfiguration *

* Generate an effective web.xml from a WebAppContext, including all components * from web.xml, web-fragment.xmls annotations etc. @@ -81,10 +83,9 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration protected final boolean _abort; protected String _originAttribute; - protected boolean _generateOrigin; protected int _count; protected Resource _quickStartWebXml; - + public QuickStartGeneratorConfiguration() { this(false); @@ -116,22 +117,6 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration return _originAttribute; } - /** - * @return the generateOrigin - */ - public boolean isGenerateOrigin() - { - return _generateOrigin; - } - - /** - * @param generateOrigin the generateOrigin to set - */ - public void setGenerateOrigin(boolean generateOrigin) - { - _generateOrigin = generateOrigin; - } - public Resource getQuickStartWebXml() { return _quickStartWebXml; @@ -163,8 +148,6 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration if (context.getBaseResource() == null) throw new IllegalArgumentException("No base resource for " + this); - LOG.info("Quickstart generating"); - MetaData md = context.getMetaData(); Map webappAttr = new HashMap<>(); @@ -195,13 +178,13 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration //the META-INF/resources discovered addContextParamFromAttribute(context, out, MetaInfConfiguration.METAINF_RESOURCES, normalizer); - // the default-context-path, if presernt + // the default-context-path, if present String defaultContextPath = (String)context.getAttribute("default-context-path"); if (defaultContextPath != null) out.tag("default-context-path", defaultContextPath); //add the name of the origin attribute, if it is being used - if (_generateOrigin) + if (StringUtil.isNotBlank(_originAttribute)) { out.openTag("context-param") .tag("param-name", ORIGIN) @@ -766,7 +749,7 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration */ public Map origin(MetaData md, String name) { - if (!(_generateOrigin || LOG.isDebugEnabled())) + if (StringUtil.isBlank(_originAttribute)) return Collections.emptyMap(); if (name == null) return Collections.emptyMap(); @@ -792,13 +775,19 @@ public class QuickStartGeneratorConfiguration extends AbstractConfiguration { MetaData metadata = context.getMetaData(); metadata.resolve(context); - - Resource quickStartWebXml = _quickStartWebXml; - if (_quickStartWebXml == null) - quickStartWebXml = context.getWebInf().addPath("/quickstart-web.xml"); - try (FileOutputStream fos = new FileOutputStream(quickStartWebXml.getFile(), false)) + try (FileOutputStream fos = new FileOutputStream(_quickStartWebXml.getFile(), false)) { generateQuickStartWebXml(context, fos); + LOG.info("Generated {}", _quickStartWebXml); + if (context.getAttribute(WebInfConfiguration.TEMPORARY_RESOURCE_BASE) != null && !context.isPersistTempDirectory()) + LOG.warn("Generated to non persistent location: " + _quickStartWebXml); } } + + @Override + public void deconfigure(WebAppContext context) throws Exception + { + super.deconfigure(context); + } + } diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartWebApp.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartWebApp.java deleted file mode 100644 index 0cf95f8f1b5..00000000000 --- a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/QuickStartWebApp.java +++ /dev/null @@ -1,90 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. -// ------------------------------------------------------------------------ -// All rights reserved. This program and the accompanying materials -// are made available under the terms of the Eclipse Public License v1.0 -// and Apache License v2.0 which accompanies this distribution. -// -// The Eclipse Public License is available at -// http://www.eclipse.org/legal/epl-v10.html -// -// The Apache License v2.0 is available at -// http://www.opensource.org/licenses/apache2.0.php -// -// You may elect to redistribute this code under either of these licenses. -// ======================================================================== -// - -package org.eclipse.jetty.quickstart; - -import org.eclipse.jetty.annotations.AnnotationConfiguration; -import org.eclipse.jetty.plus.webapp.EnvConfiguration; -import org.eclipse.jetty.plus.webapp.PlusConfiguration; -import org.eclipse.jetty.quickstart.QuickStartConfiguration.Mode; -import org.eclipse.jetty.webapp.WebAppContext; - -/** - * QuickStartWar - */ -public class QuickStartWebApp extends WebAppContext -{ - private final QuickStartConfiguration _quickStartConfiguration; - - private String _originAttribute; - private boolean _generateOrigin; - - public QuickStartWebApp() - { - super(); - addConfiguration( - _quickStartConfiguration = new QuickStartConfiguration(), - new EnvConfiguration(), - new PlusConfiguration(), - new AnnotationConfiguration()); - setExtractWAR(true); - setCopyWebDir(false); - setCopyWebInf(false); - } - - public void setOriginAttribute(String name) - { - setAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE, name); - } - - /** - * @return the originAttribute - */ - public String getOriginAttribute() - { - Object attr = getAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE); - return attr == null ? null : attr.toString(); - } - - /** - * @param generateOrigin the generateOrigin to set - */ - public void setGenerateOrigin(boolean generateOrigin) - { - setAttribute(QuickStartConfiguration.GENERATE_ORIGIN, generateOrigin); - } - - /** - * @return the generateOrigin - */ - public boolean isGenerateOrigin() - { - Object attr = getAttribute(QuickStartConfiguration.GENERATE_ORIGIN); - return attr == null ? false : Boolean.valueOf(attr.toString()); - } - - public Mode getMode() - { - return _quickStartConfiguration.getMode(); - } - - public void setMode(Mode mode) - { - _quickStartConfiguration.setMode(mode); - } -} diff --git a/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/TestQuickStart.java b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/TestQuickStart.java index 6e7b3ed3834..82e0c376f8f 100644 --- a/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/TestQuickStart.java +++ b/jetty-quickstart/src/test/java/org/eclipse/jetty/quickstart/TestQuickStart.java @@ -27,6 +27,7 @@ import org.eclipse.jetty.servlet.ListenerHolder; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.toolchain.test.FS; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.webapp.WebAppContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -61,10 +62,11 @@ public class TestQuickStart Server server = new Server(); //generate a quickstart-web.xml - QuickStartWebApp quickstart = new QuickStartWebApp(); + WebAppContext quickstart = new WebAppContext(); + quickstart.addConfiguration(new QuickStartConfiguration()); + quickstart.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.GENERATE); + quickstart.setAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE, "origin"); quickstart.setResourceBase(testDir.getAbsolutePath()); - quickstart.setMode(QuickStartConfiguration.Mode.GENERATE); - quickstart.setGenerateOrigin(true); ServletHolder fooHolder = new ServletHolder(); fooHolder.setServlet(new FooServlet()); fooHolder.setName("foo"); @@ -73,19 +75,22 @@ public class TestQuickStart lholder.setListener(new FooContextListener()); quickstart.getServletHandler().addListener(lholder); server.setHandler(quickstart); + server.setDryRun(true); server.start(); - server.stop(); assertTrue(quickstartXml.exists()); //now run the webapp again purely from the generated quickstart - QuickStartWebApp webapp = new QuickStartWebApp(); + WebAppContext webapp = new WebAppContext(); webapp.setResourceBase(testDir.getAbsolutePath()); - webapp.setMode(QuickStartConfiguration.Mode.QUICKSTART); + webapp.addConfiguration(new QuickStartConfiguration()); + webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART); webapp.setClassLoader(new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader())); server.setHandler(webapp); + server.setDryRun(false); server.start(); + server.dumpStdErr(); //verify that FooServlet is now mapped to / and not the DefaultServlet ServletHolder sh = webapp.getServletHandler().getMappedServlet("/").getResource(); @@ -104,28 +109,30 @@ public class TestQuickStart Server server = new Server(); // generate a quickstart-web.xml - QuickStartWebApp quickstart = new QuickStartWebApp(); + WebAppContext quickstart = new WebAppContext(); quickstart.setResourceBase(testDir.getAbsolutePath()); - quickstart.setMode(QuickStartConfiguration.Mode.GENERATE); - quickstart.setGenerateOrigin(true); + quickstart.addConfiguration(new QuickStartConfiguration()); + quickstart.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.GENERATE); + quickstart.setAttribute(QuickStartConfiguration.ORIGIN_ATTRIBUTE, "origin"); quickstart.setDescriptor(MavenTestingUtils.getTestResourceFile("web.xml").getAbsolutePath()); quickstart.setContextPath("/foo"); server.setHandler(quickstart); + server.setDryRun(true); server.start(); - assertEquals("/foo", quickstart.getContextPath()); assertFalse(quickstart.isContextPathDefault()); - server.stop(); assertTrue(quickstartXml.exists()); // quick start - QuickStartWebApp webapp = new QuickStartWebApp(); + WebAppContext webapp = new WebAppContext(); + webapp.addConfiguration(new QuickStartConfiguration()); + quickstart.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART); webapp.setResourceBase(testDir.getAbsolutePath()); - webapp.setMode(QuickStartConfiguration.Mode.QUICKSTART); webapp.setClassLoader(new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader())); server.setHandler(webapp); + server.setDryRun(false); server.start(); // verify the context path is the default-context-path diff --git a/jetty-security/src/test/java/org/eclipse/jetty/security/SessionAuthenticationTest.java b/jetty-security/src/test/java/org/eclipse/jetty/security/SessionAuthenticationTest.java new file mode 100644 index 00000000000..a30d3337524 --- /dev/null +++ b/jetty-security/src/test/java/org/eclipse/jetty/security/SessionAuthenticationTest.java @@ -0,0 +1,93 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.security; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import org.eclipse.jetty.security.authentication.SessionAuthentication; +import org.eclipse.jetty.server.UserIdentity; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.util.security.Password; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * SessionAuthenticationTest + * + */ +public class SessionAuthenticationTest +{ + /** + * Check that a SessionAuthenticator is serializable, and that + * the deserialized SessionAuthenticator contains the same authentication + * and authorization information. + */ + @Test + public void testSessionAuthenticationSerialization() + throws Exception + { + + ContextHandler contextHandler = new ContextHandler(); + SecurityHandler securityHandler = new ConstraintSecurityHandler(); + contextHandler.setHandler(securityHandler); + TestLoginService loginService = new TestLoginService("SessionAuthTest"); + Password pwd = new Password("foo"); + loginService.putUser("foo", pwd, new String[]{"boss", "worker"}); + securityHandler.setLoginService(loginService); + securityHandler.setAuthMethod("FORM"); + UserIdentity user = loginService.login("foo", pwd, null); + assertNotNull(user); + assertNotNull(user.getUserPrincipal()); + assertEquals("foo", user.getUserPrincipal().getName()); + SessionAuthentication sessionAuth = new SessionAuthentication("FORM", user, pwd); + assertTrue(sessionAuth.isUserInRole(null, "boss")); + contextHandler.handle(new Runnable() + { + public void run() + { + try + { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(sessionAuth); + oos.close(); + ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())); + SessionAuthentication reactivatedSessionAuth = (SessionAuthentication)ois.readObject(); + assertNotNull(reactivatedSessionAuth); + assertNotNull(reactivatedSessionAuth.getUserIdentity()); + assertNotNull(reactivatedSessionAuth.getUserIdentity().getUserPrincipal()); + assertEquals("foo", reactivatedSessionAuth.getUserIdentity().getUserPrincipal().getName()); + assertNotNull(reactivatedSessionAuth.getUserIdentity().getSubject()); + assertTrue(reactivatedSessionAuth.isUserInRole(null, "boss")); + } + catch (Exception e) + { + fail(e); + } + } + }); + } +} diff --git a/jetty-server/src/main/config/etc/sessions/session-cache-hash.xml b/jetty-server/src/main/config/etc/sessions/session-cache-hash.xml index c40486e363e..35d7bfb28f0 100644 --- a/jetty-server/src/main/config/etc/sessions/session-cache-hash.xml +++ b/jetty-server/src/main/config/etc/sessions/session-cache-hash.xml @@ -5,15 +5,16 @@ - + - - - - + + + + + diff --git a/jetty-server/src/main/config/etc/sessions/session-cache-null.xml b/jetty-server/src/main/config/etc/sessions/session-cache-null.xml index 466402975aa..7de90393a52 100644 --- a/jetty-server/src/main/config/etc/sessions/session-cache-null.xml +++ b/jetty-server/src/main/config/etc/sessions/session-cache-null.xml @@ -10,13 +10,9 @@ - - - - - - - + + + diff --git a/jetty-server/src/main/config/modules/session-cache-hash.mod b/jetty-server/src/main/config/modules/session-cache-hash.mod index 32ab705c7a2..2d336bc1d99 100644 --- a/jetty-server/src/main/config/modules/session-cache-hash.mod +++ b/jetty-server/src/main/config/modules/session-cache-hash.mod @@ -1,10 +1,9 @@ DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html [description] -Enable first level session cache in ConcurrentHashMap. -If not enabled, sessions will use a HashSessionCache by default, so enabling -via this module is only needed if the configuration properties need to be -changed. +Enable first level session cache. If this module is not enabled, sessions will +use the DefaultSessionCache by default, so enabling via this module is only needed +if the configuration properties need to be changed from their defaults. [tags] session @@ -23,3 +22,4 @@ etc/sessions/session-cache-hash.xml #jetty.session.saveOnInactiveEvict=false #jetty.session.saveOnCreate=false #jetty.session.removeUnloadableSessions=false +#jetty.session.flushOnResponseCommit=false diff --git a/jetty-server/src/main/config/modules/session-cache-null.mod b/jetty-server/src/main/config/modules/session-cache-null.mod index 6069c8f8168..2a94f59cb82 100644 --- a/jetty-server/src/main/config/modules/session-cache-null.mod +++ b/jetty-server/src/main/config/modules/session-cache-null.mod @@ -18,4 +18,4 @@ etc/sessions/session-cache-null.xml [ini-template] #jetty.session.saveOnCreate=false #jetty.session.removeUnloadableSessions=false -#jetty.session.writeThroughMode=ON_EXIT +#jetty.session.flushOnResponseCommit=false diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java index 55d155601ad..a6255f2ed72 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java @@ -858,15 +858,15 @@ public class HttpChannel implements Runnable, HttpOutput.Interceptor if (response == null) response = _response.newResponseMetaData(); commit(response); - + _combinedListener.onResponseBegin(_request); + _request.onResponseCommit(); + // wrap callback to process 100 responses final int status = response.getStatus(); final Callback committed = (status < HttpStatus.OK_200 && status >= HttpStatus.CONTINUE_100) ? new Send100Callback(callback) : new SendCallback(callback, content, true, complete); - _combinedListener.onResponseBegin(_request); - // committing write _transport.send(_request.getMetaData(), response, content, complete, committed); } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java index 55444641720..4471850c5d6 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java @@ -230,7 +230,7 @@ public class Request implements HttpServletRequest private long _timeStamp; private MultiParts _multiParts; //if the request is a multi-part mime private AsyncContextState _async; - private List _sessions; //list of sessions used during lifetime of request + private List _sessions; //list of sessions used during lifetime of request public Request(HttpChannel channel, HttpInput input) { @@ -363,32 +363,41 @@ public class Request implements HttpServletRequest */ public void enterSession(HttpSession s) { - if (s == null) + if (!(s instanceof Session)) return; if (_sessions == null) _sessions = new ArrayList<>(); if (LOG.isDebugEnabled()) LOG.debug("Request {} entering session={}", this, s); - _sessions.add(s); + _sessions.add((Session)s); } /** * Complete this request's access to a session. * - * @param s the session + * @param session the session */ - private void leaveSession(HttpSession s) + private void leaveSession(Session session) { - if (s == null) - return; - - Session session = (Session)s; if (LOG.isDebugEnabled()) LOG.debug("Request {} leaving session {}", this, session); session.getSessionHandler().complete(session); } + /** + * A response is being committed for a session, + * potentially write the session out before the + * client receives the response. + * @param session the session + */ + private void commitSession(Session session) + { + if (LOG.isDebugEnabled()) + LOG.debug("Response {} committing for session {}", this, session); + session.getSessionHandler().commit(session); + } + private MultiMap getParameters() { if (!_contentParamsExtracted) @@ -1490,13 +1499,26 @@ public class Request implements HttpServletRequest */ public void onCompleted() { - if (_sessions != null && _sessions.size() > 0) + if (_sessions != null) { - for (HttpSession s:_sessions) + for (Session s:_sessions) leaveSession(s); } } + /** + * Called when a response is about to be committed, ie sent + * back to the client + */ + public void onResponseCommit() + { + if (_sessions != null) + { + for (Session s:_sessions) + commitSession(s); + } + } + /** * Find a session that this request has already entered for the * given SessionHandler diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java index 4618a8edf2f..8456899589a 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java @@ -79,10 +79,11 @@ public class Server extends HandlerWrapper implements Attributes private final List _connectors = new CopyOnWriteArrayList<>(); private SessionIdManager _sessionIdManager; private boolean _stopAtShutdown; - private boolean _dumpAfterStart = false; - private boolean _dumpBeforeStop = false; + private boolean _dumpAfterStart; + private boolean _dumpBeforeStop; private ErrorHandler _errorHandler; private RequestLog _requestLog; + private boolean _dryRun; private final AutoLock _dateLock = new AutoLock(); private volatile DateField _dateField; @@ -130,6 +131,16 @@ public class Server extends HandlerWrapper implements Attributes setServer(this); } + public boolean isDryRun() + { + return _dryRun; + } + + public void setDryRun(boolean dryRun) + { + _dryRun = dryRun; + } + public RequestLog getRequestLog() { return _requestLog; @@ -366,25 +377,33 @@ public class Server extends HandlerWrapper implements Attributes MultiException mex = new MultiException(); // Open network connector to ensure ports are available - _connectors.stream().filter(NetworkConnector.class::isInstance).map(NetworkConnector.class::cast).forEach(connector -> + if (!_dryRun) { - try + _connectors.stream().filter(NetworkConnector.class::isInstance).map(NetworkConnector.class::cast).forEach(connector -> { - connector.open(); - } - catch (Throwable th) - { - mex.add(th); - } - }); - - // Throw now if verified start sequence and there was an open exception - mex.ifExceptionThrow(); + try + { + connector.open(); + } + catch (Throwable th) + { + mex.add(th); + } + }); + // Throw now if verified start sequence and there was an open exception + mex.ifExceptionThrow(); + } // Start the server and components, but not connectors! // #start(LifeCycle) is overridden so that connectors are not started super.doStart(); + if (_dryRun) + { + LOG.info(String.format("Started(dry run) %s @%dms", this, Uptime.getUptime())); + throw new StopException(); + } + // start connectors for (Connector connector : _connectors) { @@ -401,7 +420,7 @@ public class Server extends HandlerWrapper implements Attributes } mex.ifExceptionThrow(); - LOG.info(String.format("Started @%dms", Uptime.getUptime())); + LOG.info(String.format("Started %s @%dms", this, Uptime.getUptime())); } catch (Throwable th) { @@ -422,7 +441,7 @@ public class Server extends HandlerWrapper implements Attributes } finally { - if (isDumpAfterStart()) + if (isDumpAfterStart() && !(_dryRun && isDumpBeforeStop())) dumpStdErr(); } } @@ -441,6 +460,7 @@ public class Server extends HandlerWrapper implements Attributes if (isDumpBeforeStop()) dumpStdErr(); + LOG.info(String.format("Stopped %s", this)); if (LOG.isDebugEnabled()) LOG.debug("doStop {}", this); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandler.java index cb2e2257eb7..b02064d54a3 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandler.java @@ -123,7 +123,7 @@ public abstract class AbstractHandler extends ContainerLifeCycle implements Hand if (_server == server) return; if (isStarted()) - throw new IllegalStateException(STARTED); + throw new IllegalStateException(getState()); _server = server; } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandlerContainer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandlerContainer.java index e56645f5dcf..5f533d6f549 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandlerContainer.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/AbstractHandlerContainer.java @@ -128,7 +128,7 @@ public abstract class AbstractHandlerContainer extends AbstractHandler implement return; if (isStarted()) - throw new IllegalStateException(STARTED); + throw new IllegalStateException(getState()); super.setServer(server); Handler[] handlers = getHandlers(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java index 4840dbeff33..2a3c97d2874 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java @@ -126,7 +126,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu public static final int DEFAULT_LISTENER_TYPE_INDEX = 1; public static final int EXTENDED_LISTENER_TYPE_INDEX = 0; - private static final String __unimplmented = "Unimplemented - use org.eclipse.jetty.servlet.ServletContextHandler"; + private static final String UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER = "Unimplemented {} - use org.eclipse.jetty.servlet.ServletContextHandler"; private static final Logger LOG = Log.getLogger(ContextHandler.class); @@ -2231,7 +2231,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu @Override public void log(String message, Throwable throwable) { - _logger.warn(message, throwable); + if (throwable == null) + _logger.warn(message); + else + _logger.warn(message, throwable); } /* @@ -2491,7 +2494,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu @Override public JspConfigDescriptor getJspConfigDescriptor() { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getJspConfigDescriptor()"); return null; } @@ -2684,138 +2687,141 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu @Override public Dynamic addFilter(String filterName, Class filterClass) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addFilter(String, Class)"); return null; } @Override public Dynamic addFilter(String filterName, Filter filter) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addFilter(String, Filter)"); return null; } @Override public Dynamic addFilter(String filterName, String className) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addFilter(String, String)"); return null; } @Override public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Class servletClass) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addServlet(String, Class)"); return null; } @Override public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addServlet(String, Servlet)"); return null; } @Override public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, String className) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addServlet(String, String)"); return null; } + /** + * @since Servlet 4.0 + */ @Override public ServletRegistration.Dynamic addJspFile(String servletName, String jspFile) { // TODO new in 4.0 - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addJspFile(String, String)"); return null; } @Override public T createFilter(Class c) throws ServletException { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "createFilter(Class)"); return null; } @Override public T createServlet(Class c) throws ServletException { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "createServlet(Class)"); return null; } @Override public Set getDefaultSessionTrackingModes() { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getDefaultSessionTrackingModes()"); return null; } @Override public Set getEffectiveSessionTrackingModes() { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getEffectiveSessionTrackingModes()"); return null; } @Override public FilterRegistration getFilterRegistration(String filterName) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getFilterRegistration(String)"); return null; } @Override public Map getFilterRegistrations() { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getFilterRegistrations()"); return null; } @Override public ServletRegistration getServletRegistration(String servletName) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getServletRegistration(String)"); return null; } @Override public Map getServletRegistrations() { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getServletRegistrations()"); return null; } @Override public SessionCookieConfig getSessionCookieConfig() { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getSessionCookieConfig()"); return null; } @Override public void setSessionTrackingModes(Set sessionTrackingModes) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "setSessionTrackingModes(Set)"); } @Override public void addListener(String className) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addListener(String)"); } @Override public void addListener(T t) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addListener(T)"); } @Override public void addListener(Class listenerClass) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "addListener(Class)"); } @Override @@ -2862,14 +2868,14 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu @Override public JspConfigDescriptor getJspConfigDescriptor() { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getJspConfigDescriptor()"); return null; } @Override public void declareRoles(String... roleNames) { - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "declareRoles(String...)"); } @Override @@ -2878,49 +2884,67 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu return null; } + /** + * @since Servlet 4.0 + */ @Override public int getSessionTimeout() { // TODO new in 4.0 - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getSessionTimeout()"); return 0; } + /** + * @since Servlet 4.0 + */ @Override public void setSessionTimeout(int sessionTimeout) { // TODO new in 4.0 - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "setSessionTimeout(int)"); } + /** + * @since Servlet 4.0 + */ @Override public String getRequestCharacterEncoding() { // TODO new in 4.0 - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getRequestCharacterEncoding()"); return null; } + /** + * @since Servlet 4.0 + */ @Override public void setRequestCharacterEncoding(String encoding) { // TODO new in 4.0 - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "setRequestCharacterEncoding(String)"); } + /** + * @since Servlet 4.0 + */ @Override public String getResponseCharacterEncoding() { // TODO new in 4.0 - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "getResponseCharacterEncoding()"); return null; } + /** + * @since Servlet 4.0 + */ @Override public void setResponseCharacterEncoding(String encoding) { // TODO new in 4.0 - LOG.warn(__unimplmented); + LOG.warn(UNIMPLEMENTED_USE_SERVLET_CONTEXT_HANDLER, "setResponseCharacterEncoding(String)"); } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java index dfea9fc93b7..9bbda66b116 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerCollection.java @@ -82,7 +82,7 @@ public class HandlerCollection extends AbstractHandlerContainer public void setHandlers(Handler[] handlers) { if (!_mutableWhenRunning && isStarted()) - throw new IllegalStateException(STARTED); + throw new IllegalStateException(getState()); while (true) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java index 27a53fecf98..0c0897f5c1f 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/HandlerWrapper.java @@ -74,7 +74,7 @@ public class HandlerWrapper extends AbstractHandlerContainer public void setHandler(Handler handler) { if (isStarted()) - throw new IllegalStateException(STARTED); + throw new IllegalStateException(getState()); // check for loops if (handler == this || (handler instanceof HandlerContainer && diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCache.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCache.java index 8cac60600cc..329a44bec48 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCache.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCache.java @@ -91,6 +91,12 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements * deleted from the SessionDataStore. */ protected boolean _removeUnloadableSessions; + + /** + * If true, when a response is about to be committed back to the client, + * a dirty session will be flushed to the session store. + */ + protected boolean _flushOnResponseCommit; /** * Create a new Session object from pre-existing session data @@ -302,6 +308,18 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements _removeUnloadableSessions = removeUnloadableSessions; } + @Override + public void setFlushOnResponseCommit(boolean flushOnResponseCommit) + { + _flushOnResponseCommit = flushOnResponseCommit; + } + + @Override + public boolean isFlushOnResponseCommit() + { + return _flushOnResponseCommit; + } + /** * Get a session object. * @@ -505,6 +523,42 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements } } + /** + * A response that has accessed this session is about to + * be returned to the client. Pass the session to the store + * to persist, so that any changes will be visible to + * subsequent requests on the same node (if using NullSessionCache), + * or on other nodes. + */ + @Override + public void commit(Session session) throws Exception + { + if (session == null) + return; + + try (AutoLock lock = session.lock()) + { + //only write the session out at this point if the attributes changed. If only + //the lastAccess/expiry time changed defer the write until the last request exits + if (session.getSessionData().isDirty() && _flushOnResponseCommit) + { + if (LOG.isDebugEnabled()) + LOG.debug("Flush session {} on response commit", session); + //save the session + if (!_sessionDataStore.isPassivating()) + { + _sessionDataStore.store(session.getId(), session.getSessionData()); + } + else + { + session.willPassivate(); + _sessionDataStore.store(session.getId(), session.getSessionData()); + session.didActivate(); + } + } + } + } + @Override public void put(String id, Session session) throws Exception { @@ -730,6 +784,8 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements if (_sessionDataStore.isPassivating()) session.willPassivate(); + //Fake being dirty to force the write + session.getSessionData().setDirty(true); _sessionDataStore.store(session.getId(), session.getSessionData()); } @@ -739,7 +795,6 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements catch (Exception e) { LOG.warn("Passivation of idle session {} failed", session.getId(), e); - //session.updateInactivityTimer(); } } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCacheFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCacheFactory.java new file mode 100644 index 00000000000..b29e5585a19 --- /dev/null +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCacheFactory.java @@ -0,0 +1,114 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.server.session; + +/** + * AbstractSessionCacheFactory + * + * Base class for SessionCacheFactories. + * + */ +public abstract class AbstractSessionCacheFactory implements SessionCacheFactory +{ + int _evictionPolicy; + boolean _saveOnInactiveEvict; + boolean _saveOnCreate; + boolean _removeUnloadableSessions; + boolean _flushOnResponseCommit; + + /** + * @return the flushOnResponseCommit + */ + public boolean isFlushOnResponseCommit() + { + return _flushOnResponseCommit; + } + + /** + * @param flushOnResponseCommit the flushOnResponseCommit to set + */ + public void setFlushOnResponseCommit(boolean flushOnResponseCommit) + { + _flushOnResponseCommit = flushOnResponseCommit; + } + + /** + * @return the saveOnCreate + */ + public boolean isSaveOnCreate() + { + return _saveOnCreate; + } + + /** + * @param saveOnCreate the saveOnCreate to set + */ + public void setSaveOnCreate(boolean saveOnCreate) + { + _saveOnCreate = saveOnCreate; + } + + /** + * @return the removeUnloadableSessions + */ + public boolean isRemoveUnloadableSessions() + { + return _removeUnloadableSessions; + } + + /** + * @param removeUnloadableSessions the removeUnloadableSessions to set + */ + public void setRemoveUnloadableSessions(boolean removeUnloadableSessions) + { + _removeUnloadableSessions = removeUnloadableSessions; + } + + /** + * @return the evictionPolicy + */ + public int getEvictionPolicy() + { + return _evictionPolicy; + } + + /** + * @param evictionPolicy the evictionPolicy to set + */ + public void setEvictionPolicy(int evictionPolicy) + { + _evictionPolicy = evictionPolicy; + } + + /** + * @return the saveOnInactiveEvict + */ + public boolean isSaveOnInactiveEvict() + { + return _saveOnInactiveEvict; + } + + /** + * @param saveOnInactiveEvict the saveOnInactiveEvict to set + */ + public void setSaveOnInactiveEvict(boolean saveOnInactiveEvict) + { + _saveOnInactiveEvict = saveOnInactiveEvict; + } +} diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java index 17ce67038ce..2d7c53c1093 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/AbstractSessionDataStore.java @@ -129,10 +129,14 @@ public abstract class AbstractSessionDataStore extends ContainerLifeCycle implem long savePeriodMs = (_savePeriodSec <= 0 ? 0 : TimeUnit.SECONDS.toMillis(_savePeriodSec)); if (LOG.isDebugEnabled()) - LOG.debug("Store: id={}, dirty={}, lsave={}, period={}, elapsed={}", id, data.isDirty(), data.getLastSaved(), savePeriodMs, (System.currentTimeMillis() - lastSave)); + { + LOG.debug("Store: id={}, mdirty={}, dirty={}, lsave={}, period={}, elapsed={}", id, data.isMetaDataDirty(), + data.isDirty(), data.getLastSaved(), savePeriodMs, (System.currentTimeMillis() - lastSave)); + } - //save session if attribute changed or never been saved or time between saves exceeds threshold - if (data.isDirty() || (lastSave <= 0) || ((System.currentTimeMillis() - lastSave) >= savePeriodMs)) + //save session if attribute changed, never been saved or metadata changed (eg expiry time) and save interval exceeded + if (data.isDirty() || (lastSave <= 0) || + (data.isMetaDataDirty() && ((System.currentTimeMillis() - lastSave) >= savePeriodMs))) { //set the last saved time to now data.setLastSaved(System.currentTimeMillis()); @@ -140,7 +144,7 @@ public abstract class AbstractSessionDataStore extends ContainerLifeCycle implem { //call the specific store method, passing in previous save time doStore(id, data, lastSave); - data.setDirty(false); //only undo the dirty setting if we saved it + data.clean(); //unset all dirty flags } catch (Exception e) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/DefaultSessionCacheFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/DefaultSessionCacheFactory.java index b1261647414..87b945e5a0b 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/DefaultSessionCacheFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/DefaultSessionCacheFactory.java @@ -23,80 +23,8 @@ package org.eclipse.jetty.server.session; * * Factory for creating new DefaultSessionCaches. */ -public class DefaultSessionCacheFactory implements SessionCacheFactory +public class DefaultSessionCacheFactory extends AbstractSessionCacheFactory { - int _evictionPolicy; - boolean _saveOnInactiveEvict; - boolean _saveOnCreate; - boolean _removeUnloadableSessions; - - /** - * @return the saveOnCreate - */ - public boolean isSaveOnCreate() - { - return _saveOnCreate; - } - - /** - * @param saveOnCreate the saveOnCreate to set - */ - public void setSaveOnCreate(boolean saveOnCreate) - { - _saveOnCreate = saveOnCreate; - } - - /** - * @return the removeUnloadableSessions - */ - public boolean isRemoveUnloadableSessions() - { - return _removeUnloadableSessions; - } - - /** - * @param removeUnloadableSessions the removeUnloadableSessions to set - */ - public void setRemoveUnloadableSessions(boolean removeUnloadableSessions) - { - _removeUnloadableSessions = removeUnloadableSessions; - } - - /** - * @return the evictionPolicy - */ - public int getEvictionPolicy() - { - return _evictionPolicy; - } - - /** - * @param evictionPolicy the evictionPolicy to set - */ - public void setEvictionPolicy(int evictionPolicy) - { - _evictionPolicy = evictionPolicy; - } - - /** - * @return the saveOnInactiveEvict - */ - public boolean isSaveOnInactiveEvict() - { - return _saveOnInactiveEvict; - } - - /** - * @param saveOnInactiveEvict the saveOnInactiveEvict to set - */ - public void setSaveOnInactiveEvict(boolean saveOnInactiveEvict) - { - _saveOnInactiveEvict = saveOnInactiveEvict; - } - - /** - * @see org.eclipse.jetty.server.session.SessionCacheFactory#getSessionCache(org.eclipse.jetty.server.session.SessionHandler) - */ @Override public SessionCache getSessionCache(SessionHandler handler) { @@ -105,6 +33,7 @@ public class DefaultSessionCacheFactory implements SessionCacheFactory cache.setSaveOnInactiveEviction(isSaveOnInactiveEvict()); cache.setSaveOnCreate(isSaveOnCreate()); cache.setRemoveUnloadableSessions(isRemoveUnloadableSessions()); + cache.setFlushOnResponseCommit(isFlushOnResponseCommit()); return cache; } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionCache.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionCache.java index e22919eed7b..c589aab4bdb 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionCache.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionCache.java @@ -18,12 +18,7 @@ package org.eclipse.jetty.server.session; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSessionAttributeListener; -import javax.servlet.http.HttpSessionBindingEvent; /** * NullSessionCache @@ -35,151 +30,6 @@ import javax.servlet.http.HttpSessionBindingEvent; */ public class NullSessionCache extends AbstractSessionCache { - /** - * If the writethrough mode is ALWAYS or NEW, then use an - * attribute listener to ascertain when the attribute has changed. - * - */ - public class WriteThroughAttributeListener implements HttpSessionAttributeListener - { - Set _sessionsBeingWritten = ConcurrentHashMap.newKeySet(); - - @Override - public void attributeAdded(HttpSessionBindingEvent event) - { - doAttributeChanged(event); - } - - @Override - public void attributeRemoved(HttpSessionBindingEvent event) - { - doAttributeChanged(event); - } - - @Override - public void attributeReplaced(HttpSessionBindingEvent event) - { - doAttributeChanged(event); - } - - private void doAttributeChanged(HttpSessionBindingEvent event) - { - if (_writeThroughMode == WriteThroughMode.ON_EXIT) - return; - - Session session = (Session)event.getSession(); - - SessionDataStore store = getSessionDataStore(); - - if (store == null) - return; - - if (_writeThroughMode == WriteThroughMode.ALWAYS || (_writeThroughMode == WriteThroughMode.NEW && session.isNew())) - { - //ensure that a call to willPassivate doesn't result in a passivation - //listener removing an attribute, which would cause this listener to - //be called again - if (_sessionsBeingWritten.add(session)) - { - try - { - //should hold the lock on the session, but as sessions are never shared - //with the NullSessionCache, there can be no other thread modifying the - //same session at the same time (although of course there can be another - //request modifying its copy of the session data, so it is impossible - //to guarantee the order of writes). - if (store.isPassivating()) - session.willPassivate(); - store.store(session.getId(), session.getSessionData()); - if (store.isPassivating()) - session.didActivate(); - } - catch (Exception e) - { - LOG.warn("Write through of {} failed", e); - } - finally - { - _sessionsBeingWritten.remove(session); - } - } - } - } - } - - /** - * Defines the circumstances a session will be written to the backing store. - */ - public enum WriteThroughMode - { - /** - * ALWAYS means write through every attribute change. - */ - ALWAYS, - /** - * NEW means to write through every attribute change only - * while the session is freshly created, ie its id has not yet been returned to the client - */ - NEW, - /** - * ON_EXIT means write the session only when the request exits - * (which is the default behaviour of AbstractSessionCache) - */ - ON_EXIT - } - - private WriteThroughMode _writeThroughMode = WriteThroughMode.ON_EXIT; - protected WriteThroughAttributeListener _listener = null; - - /** - * @return the writeThroughMode - */ - public WriteThroughMode getWriteThroughMode() - { - return _writeThroughMode; - } - - /** - * @param writeThroughMode the writeThroughMode to set - */ - public void setWriteThroughMode(WriteThroughMode writeThroughMode) - { - if (getSessionHandler() == null) - throw new IllegalStateException("No SessionHandler"); - - //assume setting null is the same as ON_EXIT - if (writeThroughMode == null) - { - if (_listener != null) - getSessionHandler().removeEventListener(_listener); - _listener = null; - _writeThroughMode = WriteThroughMode.ON_EXIT; - return; - } - - switch (writeThroughMode) - { - case ON_EXIT: - { - if (_listener != null) - getSessionHandler().removeEventListener(_listener); - _listener = null; - break; - } - case NEW: - case ALWAYS: - { - if (_listener == null) - { - _listener = new WriteThroughAttributeListener(); - getSessionHandler().addEventListener(_listener); - } - break; - } - } - _writeThroughMode = writeThroughMode; - } - /** * @param handler The SessionHandler related to this SessionCache */ diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionCacheFactory.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionCacheFactory.java index dd4a4cd0986..40bf5f45f9d 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionCacheFactory.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/NullSessionCacheFactory.java @@ -18,75 +18,51 @@ package org.eclipse.jetty.server.session; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + /** * NullSessionCacheFactory * * Factory for NullSessionCaches. */ -public class NullSessionCacheFactory implements SessionCacheFactory +public class NullSessionCacheFactory extends AbstractSessionCacheFactory { - boolean _saveOnCreate; - boolean _removeUnloadableSessions; - NullSessionCache.WriteThroughMode _writeThroughMode; - - /** - * @return the writeThroughMode - */ - public NullSessionCache.WriteThroughMode getWriteThroughMode() + private static final Logger LOG = Log.getLogger("org.eclipse.jetty.server.session"); + + @Override + public int getEvictionPolicy() { - return _writeThroughMode; + return SessionCache.EVICT_ON_SESSION_EXIT; //never actually stored } - /** - * @param writeThroughMode the writeThroughMode to set - */ - public void setWriteThroughMode(NullSessionCache.WriteThroughMode writeThroughMode) + @Override + public void setEvictionPolicy(int evictionPolicy) { - _writeThroughMode = writeThroughMode; + if (LOG.isDebugEnabled()) + LOG.debug("Ignoring eviction policy setting for NullSessionCaches"); } - /** - * @return the saveOnCreate - */ - public boolean isSaveOnCreate() + @Override + public boolean isSaveOnInactiveEvict() { - return _saveOnCreate; + return false; //never kept in cache } - /** - * @param saveOnCreate the saveOnCreate to set - */ - public void setSaveOnCreate(boolean saveOnCreate) + @Override + public void setSaveOnInactiveEvict(boolean saveOnInactiveEvict) { - _saveOnCreate = saveOnCreate; + if (LOG.isDebugEnabled()) + LOG.debug("Ignoring eviction policy setting for NullSessionCaches"); } - /** - * @return the removeUnloadableSessions - */ - public boolean isRemoveUnloadableSessions() - { - return _removeUnloadableSessions; - } - - /** - * @param removeUnloadableSessions the removeUnloadableSessions to set - */ - public void setRemoveUnloadableSessions(boolean removeUnloadableSessions) - { - _removeUnloadableSessions = removeUnloadableSessions; - } - - /** - * @see org.eclipse.jetty.server.session.SessionCacheFactory#getSessionCache(org.eclipse.jetty.server.session.SessionHandler) - */ @Override public SessionCache getSessionCache(SessionHandler handler) { NullSessionCache cache = new NullSessionCache(handler); cache.setSaveOnCreate(isSaveOnCreate()); cache.setRemoveUnloadableSessions(isRemoveUnloadableSessions()); - cache.setWriteThroughMode(_writeThroughMode); + cache.setFlushOnResponseCommit(isFlushOnResponseCommit()); return cache; } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java index 8af585f4059..50656e17f40 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/Session.java @@ -367,16 +367,31 @@ public class Session implements SessionHandler.SessionIf */ public void didActivate() { - HttpSessionEvent event = new HttpSessionEvent(this); - for (String name : _sessionData.getKeys()) + //A passivate listener might remove a non-serializable attribute that + //the activate listener might put back in again, which would spuriously + //set the dirty bit to true, causing another round of passivate/activate + //when the request exits. The store clears the dirty bit if it does a + //save, so ensure dirty flag is set to the value determined by the store, + //not a passivation listener. + boolean dirty = getSessionData().isDirty(); + + try { - Object value = _sessionData.getAttribute(name); - if (value instanceof HttpSessionActivationListener) + HttpSessionEvent event = new HttpSessionEvent(this); + for (String name : _sessionData.getKeys()) { - HttpSessionActivationListener listener = (HttpSessionActivationListener)value; - listener.sessionDidActivate(event); + Object value = _sessionData.getAttribute(name); + if (value instanceof HttpSessionActivationListener) + { + HttpSessionActivationListener listener = (HttpSessionActivationListener)value; + listener.sessionDidActivate(event); + } } } + finally + { + getSessionData().setDirty(dirty); + } } /** @@ -494,6 +509,10 @@ public class Session implements SessionHandler.SessionIf { _sessionData.setMaxInactiveMs((long)secs * 1000L); _sessionData.calcAndSetExpiry(); + //dirty metadata writes can be skipped, but changing the + //maxinactiveinterval should write the session out because + //it may affect the session on other nodes, or on the same + //node in the case of the nullsessioncache _sessionData.setDirty(true); if (LOG.isDebugEnabled()) @@ -1059,9 +1078,6 @@ public class Session implements SessionHandler.SessionIf return _sessionData; } - /** - * - */ public void setResident(boolean resident) { _resident = resident; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionCache.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionCache.java index a9b38f95651..b47810a9225 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionCache.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionCache.java @@ -126,7 +126,7 @@ public interface SessionCache extends LifeCycle * @param id the session id * @param session the current session object * @throws Exception if any error occurred - * @deprecated @see release + * @deprecated use {@link #release(String, Session)} instead */ @Deprecated void put(String id, Session session) throws Exception; @@ -143,6 +143,17 @@ public interface SessionCache extends LifeCycle */ void release(String id, Session session) throws Exception; + /** + * Called when a response is about to be committed. The + * cache can write the session to ensure that the + * SessionDataStore contains changes to the session + * that occurred during the lifetime of the request. This + * can help ensure that if a subsequent request goes to a + * different server, it will be able to see the session + * changes via the shared store. + */ + void commit(Session session) throws Exception; + /** * Check to see if a Session is in the cache. Does NOT consult * the SessionDataStore. @@ -265,4 +276,18 @@ public interface SessionCache extends LifeCycle * @return if true unloadable session will be deleted */ boolean isRemoveUnloadableSessions(); + + /** + * If true, a dirty session will be written to the SessionDataStore + * just before a response is returned to the client. This ensures + * that subsequent requests to either the same node or a different + * node see the changed session data. + */ + void setFlushOnResponseCommit(boolean flushOnResponse); + + /** + * @return true if dirty sessions should be written + * before the response is committed. + */ + boolean isFlushOnResponseCommit(); } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionData.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionData.java index 23c862586b9..3b3a9fd0493 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionData.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionData.java @@ -58,6 +58,7 @@ public class SessionData implements Serializable protected Map _attributes; protected boolean _dirty; protected long _lastSaved; //time in msec since last save + protected boolean _metaDataDirty; //non-attribute data has changed /** * Serialize the attribute map of the session. @@ -240,6 +241,22 @@ public class SessionData implements Serializable setDirty(true); } + /** + * @return the metaDataDirty + */ + public boolean isMetaDataDirty() + { + return _metaDataDirty; + } + + /** + * @param metaDataDirty true means non-attribute data has changed + */ + public void setMetaDataDirty(boolean metaDataDirty) + { + _metaDataDirty = metaDataDirty; + } + /** * @param name the name of the attribute * @return the value of the attribute named @@ -267,6 +284,15 @@ public class SessionData implements Serializable return old; } + /** + * Clear all dirty flags. + */ + public void clean() + { + setDirty(false); + setMetaDataDirty(false); + } + public void putAllAttributes(Map attributes) { _attributes.putAll(attributes); @@ -366,11 +392,13 @@ public class SessionData implements Serializable public void calcAndSetExpiry(long time) { setExpiry(calcExpiry(time)); + setMetaDataDirty(true); } public void calcAndSetExpiry() { setExpiry(calcExpiry()); + setMetaDataDirty(true); } public long getCreated() diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java index 0d6e2c48a7f..0505da7722c 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionHandler.java @@ -354,10 +354,9 @@ public class SessionHandler extends ScopedHandler } /** - * Called by the {@link Request} when it finally finishes. + * Called when a request is finally leaving a session. * * @param session the session object - * @see #access(HttpSession, boolean) */ public void complete(HttpSession session) { @@ -377,6 +376,28 @@ public class SessionHandler extends ScopedHandler LOG.warn(e); } } + + /** + * Called when a response is about to be committed. + * We might take this opportunity to persist the session + * so that any subsequent requests to other servers + * will see the modifications. + */ + public void commit(HttpSession session) + { + if (session == null) + return; + + Session s = ((SessionIf)session).getSession(); + try + { + _sessionCache.commit(s); + } + catch (Exception e) + { + LOG.warn(e); + } + } /* * @see org.eclipse.thread.AbstractLifeCycle#doStart() diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java index e3435fdc511..55af985c57e 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java @@ -1259,9 +1259,10 @@ public class ServletHolder extends Holder implements UserIdentity.Scope try { ServletContext ctx = getServletHandler().getServletContext(); - if (ctx == null) - return getHeldClass().getDeclaredConstructor().newInstance(); - return ctx.createServlet(getHeldClass()); + if (ctx instanceof ServletContextHandler.Context) + return ctx.createServlet(getHeldClass()); + return getHeldClass().getDeclaredConstructor().newInstance(); + } catch (ServletException ex) { diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java index 9a938ad9b30..080e045eca2 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletContextHandlerTest.java @@ -667,7 +667,7 @@ public class ServletContextHandlerTest } @Test - public void testAddServletFromFilter() throws Exception + public void testAddServletByClassFromFilter() throws Exception { //A servlet cannot be added from a Filter Logger logger = Log.getLogger(ContextHandler.class.getName() + "ROOT"); @@ -718,6 +718,110 @@ public class ServletContextHandlerTest } } + @Test + public void testAddServletByInstanceFromFilter() throws Exception + { + //A servlet cannot be added from a Filter + Logger logger = Log.getLogger(ContextHandler.class.getName() + "ROOT"); + + try (StacklessLogging stackless = new StacklessLogging(logger)) + { + ServletContextHandler context = new ServletContextHandler(); + context.setLogger(logger); + FilterHolder holder = new FilterHolder(new Filter() + { + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + ServletRegistration rego = filterConfig.getServletContext().addServlet("hello", new HelloServlet()); + rego.addMapping("/hello/*"); + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + } + + @Override + public void destroy() + { + } + + }); + context.addFilter(holder, "/*", EnumSet.of(DispatcherType.REQUEST)); + context.getServletHandler().setStartWithUnavailable(false); + context.setContextPath("/"); + _server.setHandler(context); + _server.start(); + fail("Servlet can only be added from SCI or SCL"); + } + catch (Exception e) + { + if (!(e instanceof IllegalStateException)) + { + if (e instanceof ServletException) + { + assertTrue(e.getCause() instanceof IllegalStateException); + } + else + fail(e); + } + } + } + + @Test + public void testAddServletByClassNameFromFilter() throws Exception + { + //A servlet cannot be added from a Filter + Logger logger = Log.getLogger(ContextHandler.class.getName() + "ROOT"); + + try (StacklessLogging stackless = new StacklessLogging(logger)) + { + ServletContextHandler context = new ServletContextHandler(); + context.setLogger(logger); + FilterHolder holder = new FilterHolder(new Filter() + { + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + ServletRegistration rego = filterConfig.getServletContext().addServlet("hello", HelloServlet.class.getName()); + rego.addMapping("/hello/*"); + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + } + + @Override + public void destroy() + { + } + + }); + context.addFilter(holder, "/*", EnumSet.of(DispatcherType.REQUEST)); + context.getServletHandler().setStartWithUnavailable(false); + context.setContextPath("/"); + _server.setHandler(context); + _server.start(); + fail("Servlet can only be added from SCI or SCL"); + } + catch (Exception e) + { + if (!(e instanceof IllegalStateException)) + { + if (e instanceof ServletException) + { + assertTrue(e.getCause() instanceof IllegalStateException); + } + else + fail(e); + } + } + } + @Test public void testAddServletFromSCL() throws Exception { @@ -770,6 +874,7 @@ public class ServletContextHandlerTest rego.addMapping("/hello/*"); } } + root.addBean(new MySCIStarter(root.getServletContext(), new ServletAddingSCI()), true); _server.start(); @@ -797,7 +902,6 @@ public class ServletContextHandlerTest request.append("\n"); String response = _connector.getResponse(request.toString()); - int result; assertThat("Response", response, containsString("Test")); context.addServlet(HelloServlet.class, "/hello"); @@ -950,7 +1054,6 @@ public class ServletContextHandlerTest request.append("\n"); String response = _connector.getResponse(request.toString()); - int result; assertThat("Response", response, containsString("Test")); assertEquals(extra, context.getSessionHandler().getHandler()); @@ -995,7 +1098,6 @@ public class ServletContextHandlerTest request.append("\n"); String response = _connector.getResponse(request.toString()); - int result; assertThat("Response", response, containsString("Test")); context.stop(); @@ -1016,7 +1118,7 @@ public class ServletContextHandlerTest @Test public void testSetSecurityHandler() throws Exception { - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS|ServletContextHandler.SECURITY|ServletContextHandler.GZIP); + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS | ServletContextHandler.SECURITY | ServletContextHandler.GZIP); assertNotNull(context.getSessionHandler()); SessionHandler sessionHandler = context.getSessionHandler(); assertNotNull(context.getSecurityHandler()); @@ -1094,7 +1196,6 @@ public class ServletContextHandlerTest request.append("\n"); String response = _connector.getResponse(request.toString()); - int result; assertThat("Response", response, containsString("Test")); context.stop(); diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/AbstractLifeCycle.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/AbstractLifeCycle.java index a8626a2bc27..75b9aa9f74f 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/AbstractLifeCycle.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/AbstractLifeCycle.java @@ -34,27 +34,39 @@ public abstract class AbstractLifeCycle implements LifeCycle { private static final Logger LOG = Log.getLogger(AbstractLifeCycle.class); - public static final String STOPPED = "STOPPED"; - public static final String FAILED = "FAILED"; - public static final String STARTING = "STARTING"; - public static final String STARTED = "STARTED"; - public static final String STOPPING = "STOPPING"; - public static final String RUNNING = "RUNNING"; + enum State + { + STOPPED, + STARTING, + STARTED, + STOPPING, + FAILED + } + + public static final String STOPPED = State.STOPPED.toString(); + public static final String FAILED = State.FAILED.toString(); + public static final String STARTING = State.STARTING.toString(); + public static final String STARTED = State.STARTED.toString(); + public static final String STOPPING = State.STOPPING.toString(); private final CopyOnWriteArrayList _listeners = new CopyOnWriteArrayList(); private final Object _lock = new Object(); - private static final int STATE_FAILED = -1; - private static final int STATE_STOPPED = 0; - private static final int STATE_STARTING = 1; - private static final int STATE_STARTED = 2; - private static final int STATE_STOPPING = 3; - private volatile int _state = STATE_STOPPED; + private volatile State _state = State.STOPPED; private long _stopTimeout = 30000; + /** + * Method to override to start the lifecycle + * @throws StopException If thrown, the lifecycle will immediately be stopped. + * @throws Exception If there was a problem starting. Will cause a transition to FAILED state + */ protected void doStart() throws Exception { } + /** + * Method to override to stop the lifecycle + * @throws Exception If there was a problem stopping. Will cause a transition to FAILED state + */ protected void doStop() throws Exception { } @@ -66,11 +78,31 @@ public abstract class AbstractLifeCycle implements LifeCycle { try { - if (_state == STATE_STARTED || _state == STATE_STARTING) - return; - setStarting(); - doStart(); - setStarted(); + switch (_state) + { + case STARTED: + return; + + case STARTING: + case STOPPING: + throw new IllegalStateException(getState()); + + default: + try + { + setStarting(); + doStart(); + setStarted(); + } + catch (StopException e) + { + if (LOG.isDebugEnabled()) + LOG.debug(e); + setStopping(); + doStop(); + setStopped(); + } + } } catch (Throwable e) { @@ -87,11 +119,20 @@ public abstract class AbstractLifeCycle implements LifeCycle { try { - if (_state == STATE_STOPPING || _state == STATE_STOPPED) - return; - setStopping(); - doStop(); - setStopped(); + switch (_state) + { + case STOPPED: + return; + + case STARTING: + case STOPPING: + throw new IllegalStateException(getState()); + + default: + setStopping(); + doStop(); + setStopped(); + } } catch (Throwable e) { @@ -104,39 +145,45 @@ public abstract class AbstractLifeCycle implements LifeCycle @Override public boolean isRunning() { - final int state = _state; - - return state == STATE_STARTED || state == STATE_STARTING; + final State state = _state; + switch (state) + { + case STARTED: + case STARTING: + return true; + default: + return false; + } } @Override public boolean isStarted() { - return _state == STATE_STARTED; + return _state == State.STARTED; } @Override public boolean isStarting() { - return _state == STATE_STARTING; + return _state == State.STARTING; } @Override public boolean isStopping() { - return _state == STATE_STOPPING; + return _state == State.STOPPING; } @Override public boolean isStopped() { - return _state == STATE_STOPPED; + return _state == State.STOPPED; } @Override public boolean isFailed() { - return _state == STATE_FAILED; + return _state == State.FAILED; } @Override @@ -154,85 +201,73 @@ public abstract class AbstractLifeCycle implements LifeCycle @ManagedAttribute(value = "Lifecycle State for this instance", readonly = true) public String getState() { - switch (_state) - { - case STATE_FAILED: - return FAILED; - case STATE_STARTING: - return STARTING; - case STATE_STARTED: - return STARTED; - case STATE_STOPPING: - return STOPPING; - case STATE_STOPPED: - return STOPPED; - default: - return null; - } + return _state.toString(); } public static String getState(LifeCycle lc) { + if (lc instanceof AbstractLifeCycle) + return ((AbstractLifeCycle)lc)._state.toString(); if (lc.isStarting()) - return STARTING; + return State.STARTING.toString(); if (lc.isStarted()) - return STARTED; + return State.STARTED.toString(); if (lc.isStopping()) - return STOPPING; + return State.STOPPING.toString(); if (lc.isStopped()) - return STOPPED; - return FAILED; + return State.STOPPED.toString(); + return State.FAILED.toString(); } private void setStarted() { - _state = STATE_STARTED; - if (LOG.isDebugEnabled()) - LOG.debug(STARTED + " @{}ms {}", Uptime.getUptime(), this); - for (Listener listener : _listeners) + if (_state == State.STARTING) { - listener.lifeCycleStarted(this); + _state = State.STARTED; + if (LOG.isDebugEnabled()) + LOG.debug("STARTED @{}ms {}", Uptime.getUptime(), this); + for (Listener listener : _listeners) + listener.lifeCycleStarted(this); } } private void setStarting() { if (LOG.isDebugEnabled()) - LOG.debug("starting {}", this); - _state = STATE_STARTING; + LOG.debug("STARTING {}", this); + _state = State.STARTING; for (Listener listener : _listeners) - { listener.lifeCycleStarting(this); - } } private void setStopping() { if (LOG.isDebugEnabled()) - LOG.debug("stopping {}", this); - _state = STATE_STOPPING; + LOG.debug("STOPPING {}", this); + _state = State.STOPPING; for (Listener listener : _listeners) - { listener.lifeCycleStopping(this); - } } private void setStopped() { - _state = STATE_STOPPED; - if (LOG.isDebugEnabled()) - LOG.debug("{} {}", STOPPED, this); - for (Listener listener : _listeners) + if (_state == State.STOPPING) { - listener.lifeCycleStopped(this); + _state = State.STOPPED; + if (LOG.isDebugEnabled()) + LOG.debug("STOPPED {}", this); + for (Listener listener : _listeners) + { + listener.lifeCycleStopped(this); + } } } private void setFailed(Throwable th) { - _state = STATE_FAILED; + _state = State.FAILED; if (LOG.isDebugEnabled()) - LOG.warn(FAILED + " " + this + ": " + th, th); + LOG.warn("FAILED " + this + ": " + th, th); for (Listener listener : _listeners) { listener.lifeCycleFailure(this, th); @@ -290,4 +325,10 @@ public abstract class AbstractLifeCycle implements LifeCycle } return String.format("%s@%x{%s}", name, hashCode(), getState()); } + + /** + * An exception, which if thrown by doStart will immediately stop the component + */ + public class StopException extends RuntimeException + {} } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java index 188e5d65dc3..689f2a51d6f 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/ContainerLifeCycle.java @@ -100,6 +100,8 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container, { for (Bean b : _beans) { + if (!isStarting()) + break; if (b._bean instanceof LifeCycle) { LifeCycle l = (LifeCycle)b._bean; @@ -127,8 +129,6 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container, } } } - - super.doStart(); } catch (Throwable th) { @@ -193,6 +193,8 @@ public class ContainerLifeCycle extends AbstractLifeCycle implements Container, MultiException mex = new MultiException(); for (Bean b : reverse) { + if (!isStopping()) + break; if (b._managed == Managed.MANAGED && b._bean instanceof LifeCycle) { LifeCycle l = (LifeCycle)b._bean; diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java index 0758e441616..3ce9d5cbf51 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/MetaData.java @@ -403,7 +403,7 @@ public class MetaData p.process(context, getWebXml()); for (WebDescriptor wd : getOverrideWebs()) { - LOG.debug("process {} {}", context, wd); + LOG.debug("process {} {} {}", context, p, wd); p.process(context, wd); } } diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java index a25f4e399bf..99a8c3202bd 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java @@ -38,6 +38,7 @@ public class WebInfConfiguration extends AbstractConfiguration private static final Logger LOG = Log.getLogger(WebInfConfiguration.class); public static final String TEMPDIR_CONFIGURED = "org.eclipse.jetty.tmpdirConfigured"; + public static final String TEMPORARY_RESOURCE_BASE = "org.eclipse.jetty.webapp.tmpResourceBase"; protected Resource _preUnpackBaseResource; @@ -338,8 +339,11 @@ public class WebInfConfiguration extends AbstractConfiguration } if (extractedWebAppDir == null) + { // Then extract it if necessary to the temporary location extractedWebAppDir = new File(context.getTempDirectory(), "webapp"); + context.setAttribute(TEMPORARY_RESOURCE_BASE, extractedWebAppDir); + } if (webApp.getFile() != null && webApp.getFile().isDirectory()) { diff --git a/jetty-websocket/javax-websocket-common/src/test/resources/jetty-logging.properties b/jetty-websocket/javax-websocket-common/src/test/resources/jetty-logging.properties index 4aebf62b5f8..ef7003a8baa 100644 --- a/jetty-websocket/javax-websocket-common/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/javax-websocket-common/src/test/resources/jetty-logging.properties @@ -1,25 +1,5 @@ -# -# -# ======================================================================== -# Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. -# ------------------------------------------------------------------------ -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Eclipse Public License v1.0 -# and Apache License v2.0 which accompanies this distribution. -# -# The Eclipse Public License is available at -# http://www.eclipse.org/legal/epl-v10.html -# -# The Apache License v2.0 is available at -# http://www.opensource.org/licenses/apache2.0.php -# -# You may elect to redistribute this code under either of these licenses. -# ======================================================================== -# -# -# org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -org.eclipse.jetty.LEVEL=WARN +# org.eclipse.jetty.LEVEL=DEBUG # org.eclipse.jetty.util.log.stderr.LONG=true # org.eclipse.jetty.server.AbstractConnector.LEVEL=DEBUG # org.eclipse.jetty.io.WriteFlusher.LEVEL=DEBUG diff --git a/jetty-websocket/javax-websocket-server/src/test/resources/jetty-logging.properties b/jetty-websocket/javax-websocket-server/src/test/resources/jetty-logging.properties index d9e757d813a..cfafdb369a4 100644 --- a/jetty-websocket/javax-websocket-server/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/javax-websocket-server/src/test/resources/jetty-logging.properties @@ -1,11 +1,10 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -org.eclipse.jetty.LEVEL=WARN +# org.eclipse.jetty.LEVEL=DEBUG # org.eclipse.jetty.websocket.LEVEL=DEBUG # org.eclipse.jetty.websocket.LEVEL=INFO # org.eclipse.jetty.websocket.LEVEL=WARN # org.eclipse.jetty.websocket.common.io.LEVEL=DEBUG # org.eclipse.jetty.websocket.common.WebSocketSession.LEVEL=DEBUG -# org.eclipse.jetty.websocket.jsr356.LEVEL=DEBUG ### Show state changes on BrowserDebugTool # -- LEAVE THIS AT DEBUG LEVEL -- -org.eclipse.jetty.websocket.jsr356.server.browser.LEVEL=DEBUG +org.eclipse.jetty.websocket.javax.server.browser.LEVEL=DEBUG diff --git a/jetty-websocket/javax-websocket-tests/src/test/resources/jetty-logging.properties b/jetty-websocket/javax-websocket-tests/src/test/resources/jetty-logging.properties index d078063b659..e9d6afe39e7 100644 --- a/jetty-websocket/javax-websocket-tests/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/javax-websocket-tests/src/test/resources/jetty-logging.properties @@ -1,25 +1,5 @@ -# -# -# ======================================================================== -# Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. -# ------------------------------------------------------------------------ -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Eclipse Public License v1.0 -# and Apache License v2.0 which accompanies this distribution. -# -# The Eclipse Public License is available at -# http://www.eclipse.org/legal/epl-v10.html -# -# The Apache License v2.0 is available at -# http://www.opensource.org/licenses/apache2.0.php -# -# You may elect to redistribute this code under either of these licenses. -# ======================================================================== -# -# -# org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -org.eclipse.jetty.LEVEL=WARN +# org.eclipse.jetty.LEVEL=DEBUG # org.eclipse.jetty.util.log.stderr.LONG=true # org.eclipse.jetty.server.AbstractConnector.LEVEL=DEBUG # org.eclipse.jetty.io.WriteFlusher.LEVEL=DEBUG @@ -28,16 +8,8 @@ org.eclipse.jetty.LEVEL=WARN # org.eclipse.jetty.io.LEVEL=DEBUG # org.eclipse.jetty.io.ManagedSelector.LEVEL=INFO # org.eclipse.jetty.websocket.LEVEL=DEBUG -# org.eclipse.jetty.websocket.core.internal.WebSocketCoreSessionsion.LEVEL=DEBUG -# org.eclipse.jetty.websocket.jsr356.tests.LEVEL=DEBUG +# org.eclipse.jetty.websocket.core.internal.WebSocketCoreSession.LEVEL=DEBUG # org.eclipse.jetty.websocket.LEVEL=INFO -# org.eclipse.jetty.websocket.jsr356.messages.LEVEL=DEBUG # org.eclipse.jetty.websocket.tests.LEVEL=DEBUG # org.eclipse.jetty.websocket.tests.client.LEVEL=DEBUG -# org.eclipse.jetty.websocket.tests.client.jsr356.LEVEL=DEBUG -# org.eclipse.jetty.websocket.tests.server.LEVEL=DEBUG -# org.eclipse.jetty.websocket.tests.server.jsr356.LEVEL=DEBUG -### Showing any unintended (ignored) errors from CompletionCallback -# org.eclipse.jetty.websocket.common.CompletionCallback.LEVEL=ALL -### Disabling intentional error out of RFCSocket -org.eclipse.jetty.websocket.tests.server.RFCSocket.LEVEL=OFF +# org.eclipse.jetty.websocket.tests.server.LEVEL=DEBUG \ No newline at end of file diff --git a/jetty-websocket/jetty-websocket-client/src/test/resources/jetty-logging.properties b/jetty-websocket/jetty-websocket-client/src/test/resources/jetty-logging.properties index b813365ce20..b88f6f45f02 100644 --- a/jetty-websocket/jetty-websocket-client/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/jetty-websocket-client/src/test/resources/jetty-logging.properties @@ -1,5 +1,4 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -org.eclipse.jetty.LEVEL=WARN # org.eclipse.jetty.LEVEL=DEBUG # org.eclipse.jetty.io.LEVEL=INFO # org.eclipse.jetty.client.LEVEL=DEBUG @@ -8,11 +7,9 @@ org.eclipse.jetty.LEVEL=WARN # org.eclipse.jetty.websocket.LEVEL=DEBUG # org.eclipse.jetty.websocket.client.LEVEL=DEBUG # org.eclipse.jetty.websocket.client.ClientCloseTest.LEVEL=DEBUG -org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.LEVEL=DEBUG +# org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.LEVEL=DEBUG # org.eclipse.jetty.websocket.common.io.IOState.LEVEL=DEBUG # org.eclipse.jetty.websocket.common.test.LEVEL=DEBUG # org.eclipse.jetty.websocket.common.Generator.LEVEL=DEBUG -org.eclipse.jetty.websocket.common.Parser.LEVEL=DEBUG -# org.eclipse.jetty.websocket.client.TrackingSocket.LEVEL=DEBUG -### Hide the stacktraces during testing -org.eclipse.jetty.websocket.client.internal.io.UpgradeConnection.STACKS=false +# org.eclipse.jetty.websocket.common.Parser.LEVEL=DEBUG +# org.eclipse.jetty.websocket.client.TrackingSocket.LEVEL=DEBUG \ No newline at end of file diff --git a/jetty-websocket/jetty-websocket-common/src/test/resources/jetty-logging.properties b/jetty-websocket/jetty-websocket-common/src/test/resources/jetty-logging.properties index af83047e755..07faa86dbce 100644 --- a/jetty-websocket/jetty-websocket-common/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/jetty-websocket-common/src/test/resources/jetty-logging.properties @@ -1,18 +1 @@ -# -# ======================================================================== -# Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. -# ------------------------------------------------------------------------ -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Eclipse Public License v1.0 -# and Apache License v2.0 which accompanies this distribution. -# -# The Eclipse Public License is available at -# http://www.eclipse.org/legal/epl-v10.html -# -# The Apache License v2.0 is available at -# http://www.opensource.org/licenses/apache2.0.php -# -# You may elect to redistribute this code under either of these licenses. -# ======================================================================== -# org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog \ No newline at end of file diff --git a/jetty-websocket/jetty-websocket-tests/src/test/resources/jetty-logging.properties b/jetty-websocket/jetty-websocket-tests/src/test/resources/jetty-logging.properties index 8806e105177..c5aaeeb584b 100644 --- a/jetty-websocket/jetty-websocket-tests/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/jetty-websocket-tests/src/test/resources/jetty-logging.properties @@ -1,44 +1,10 @@ -# -# -# ======================================================================== -# Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. -# ------------------------------------------------------------------------ -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Eclipse Public License v1.0 -# and Apache License v2.0 which accompanies this distribution. -# -# The Eclipse Public License is available at -# http://www.eclipse.org/legal/epl-v10.html -# -# The Apache License v2.0 is available at -# http://www.opensource.org/licenses/apache2.0.php -# -# You may elect to redistribute this code under either of these licenses. -# ======================================================================== -# -# -# org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -org.eclipse.jetty.LEVEL=WARN -# org.eclipse.jetty.websocket.tests.LEVEL=DEBUG -# org.eclipse.jetty.util.log.stderr.LONG=true +# org.eclipse.jetty.LEVEL=DEBUG +# org.eclipse.jetty.websocket.LEVEL=DEBUG +# org.eclipse.jetty.websocket.test.LEVEL=DEBUG # org.eclipse.jetty.server.AbstractConnector.LEVEL=DEBUG # org.eclipse.jetty.io.WriteFlusher.LEVEL=DEBUG # org.eclipse.jetty.io.FillInterest.LEVEL=DEBUG # org.eclipse.jetty.client.LEVEL=DEBUG # org.eclipse.jetty.io.LEVEL=DEBUG -# org.eclipse.jetty.io.ManagedSelector.LEVEL=INFO -# org.eclipse.jetty.websocket.LEVEL=DEBUG -# org.eclipse.jetty.websocket.core.internal.WebSocketCoreSessionsion.LEVEL=DEBUG -# org.eclipse.jetty.websocket.jsr356.tests.LEVEL=DEBUG -# org.eclipse.jetty.websocket.LEVEL=INFO -# org.eclipse.jetty.websocket.jsr356.messages.LEVEL=DEBUG -# org.eclipse.jetty.websocket.tests.LEVEL=DEBUG -# org.eclipse.jetty.websocket.tests.client.LEVEL=DEBUG -# org.eclipse.jetty.websocket.tests.client.jsr356.LEVEL=DEBUG -# org.eclipse.jetty.websocket.tests.server.LEVEL=DEBUG -# org.eclipse.jetty.websocket.tests.server.jsr356.LEVEL=DEBUG -### Showing any unintended (ignored) errors from CompletionCallback -# org.eclipse.jetty.websocket.common.CompletionCallback.LEVEL=ALL -### Disabling intentional error out of RFCSocket -org.eclipse.jetty.websocket.tests.server.RFCSocket.LEVEL=OFF +# org.eclipse.jetty.io.ManagedSelector.LEVEL=INFO \ No newline at end of file diff --git a/jetty-websocket/websocket-core/src/test/resources/jetty-logging.properties b/jetty-websocket/websocket-core/src/test/resources/jetty-logging.properties index 039b5a51133..03fa0413842 100644 --- a/jetty-websocket/websocket-core/src/test/resources/jetty-logging.properties +++ b/jetty-websocket/websocket-core/src/test/resources/jetty-logging.properties @@ -1,22 +1,5 @@ -# -# ======================================================================== -# Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. -# ------------------------------------------------------------------------ -# All rights reserved. This program and the accompanying materials -# are made available under the terms of the Eclipse Public License v1.0 -# and Apache License v2.0 which accompanies this distribution. -# -# The Eclipse Public License is available at -# http://www.eclipse.org/legal/epl-v10.html -# -# The Apache License v2.0 is available at -# http://www.opensource.org/licenses/apache2.0.php -# -# You may elect to redistribute this code under either of these licenses. -# ======================================================================== -# org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -org.eclipse.jetty.LEVEL=WARN +# org.eclipse.jetty.LEVEL=DEBUG # org.eclipse.jetty.io.LEVEL=DEBUG # org.eclipse.jetty.websocket.core.LEVEL=DEBUG # org.eclipse.jetty.util.log.stderr.LONG=true @@ -28,6 +11,4 @@ org.eclipse.jetty.LEVEL=WARN # org.eclipse.jetty.io.ManagedSelector.LEVEL=DEBUG # org.eclipse.jetty.websocket.LEVEL=DEBUG # org.eclipse.jetty.websocket.LEVEL=INFO -# org.eclipse.jetty.websocket.core.LEVEL=DEBUG -### Showing any unintended (ignored) errors from CompletionCallback -# org.eclipse.jetty.websocket.core.CompletionCallback.LEVEL=ALL +# org.eclipse.jetty.websocket.core.LEVEL=DEBUG \ No newline at end of file diff --git a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java index dd4000c758f..de09b819320 100644 --- a/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java +++ b/jetty-xml/src/main/java/org/eclipse/jetty/xml/XmlConfiguration.java @@ -36,6 +36,7 @@ import java.nio.file.Paths; import java.security.AccessController; import java.security.PrivilegedExceptionAction; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -1747,6 +1748,8 @@ public class XmlConfiguration properties.putAll(System.getProperties()); // For all arguments, load properties + if (LOG.isDebugEnabled()) + LOG.debug("args={}", Arrays.asList(args)); for (String arg : args) { if (arg.indexOf('=') >= 0) @@ -1785,17 +1788,34 @@ public class XmlConfiguration } } + if (LOG.isDebugEnabled()) + LOG.debug("objects={}", Arrays.asList(objects)); + // For all objects created by XmlConfigurations, start them if they are lifecycles. + List started = new ArrayList<>(objects.size()); for (Object obj : objects) { if (obj instanceof LifeCycle) { LifeCycle lc = (LifeCycle)obj; if (!lc.isRunning()) + { lc.start(); + if (lc.isStarted()) + started.add(lc); + else + { + // Failed to start a component, so stop all started components + Collections.reverse(started); + for (LifeCycle slc : started) + { + slc.stop(); + } + break; + } + } } } - return null; }); } diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/BadAppTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/BadAppTests.java index 9248a2dc5e4..21b1ffc4684 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/BadAppTests.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/BadAppTests.java @@ -103,7 +103,7 @@ public class BadAppTests extends AbstractDistributionTest int port = distribution.freePort(); try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + port + "/badapp/"); @@ -143,7 +143,7 @@ public class BadAppTests extends AbstractDistributionTest int port = distribution.freePort(); try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + port + "/badapp/"); diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/CDITests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/CDITests.java index dd7d90cda29..f0d7f981e84 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/CDITests.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/CDITests.java @@ -101,7 +101,7 @@ public class CDITests extends AbstractDistributionTest int port = distribution.freePort(); try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + port + "/demo/greetings"); diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DemoBaseTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DemoBaseTests.java index 40828383857..2422bda5f76 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DemoBaseTests.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DemoBaseTests.java @@ -56,7 +56,7 @@ public class DemoBaseTests extends AbstractDistributionTest try (DistributionTester.Run run1 = distribution.start(args)) { - assertTrue(run1.awaitConsoleLogsFor("Started @", 20, TimeUnit.SECONDS)); + assertTrue(run1.awaitConsoleLogsFor("Started Server@", 20, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + httpPort + "/test/jsp/dump.jsp"); @@ -88,7 +88,7 @@ public class DemoBaseTests extends AbstractDistributionTest try (DistributionTester.Run run1 = distribution.start(args)) { - assertTrue(run1.awaitConsoleLogsFor("Started @", 20, TimeUnit.SECONDS)); + assertTrue(run1.awaitConsoleLogsFor("Started Server@", 20, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response; @@ -133,7 +133,7 @@ public class DemoBaseTests extends AbstractDistributionTest try (DistributionTester.Run run1 = distribution.start(args)) { - assertTrue(run1.awaitConsoleLogsFor("Started @", 20, TimeUnit.SECONDS)); + assertTrue(run1.awaitConsoleLogsFor("Started Server@", 20, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.POST("http://localhost:" + httpPort + "/test-spec/asy/xx").send(); diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java index 99bfbb945e0..6779e9d3ee6 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DistributionTests.java @@ -41,6 +41,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.not; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -63,7 +64,7 @@ public class DistributionTests extends AbstractDistributionTest int port = distribution.freePort(); try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + port); @@ -75,6 +76,57 @@ public class DistributionTests extends AbstractDistributionTest } } + @Test + public void testQuickStartGenerationAndRun() throws Exception + { + String jettyVersion = System.getProperty("jettyVersion"); + DistributionTester distribution = DistributionTester.Builder.newInstance() + .jettyVersion(jettyVersion) + .mavenLocalRepository(System.getProperty("mavenRepoPath")) + .build(); + + String[] args1 = { + "--create-startd", + "--approve-all-licenses", + "--add-to-start=resources,server,http,webapp,deploy,jsp,servlet,servlets,quickstart" + }; + + try (DistributionTester.Run run1 = distribution.start(args1)) + { + assertTrue(run1.awaitFor(5, TimeUnit.SECONDS)); + assertEquals(0, run1.getExitValue()); + + File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-webapp:war:" + jettyVersion); + distribution.installWarFile(war, "test"); + + + try (DistributionTester.Run run2 = distribution.start("jetty.quickstart.mode=GENERATE")) + { + assertTrue(run2.awaitConsoleLogsFor("QuickStartGeneratorConfiguration:main: Generated", 10, TimeUnit.SECONDS)); + Path unpackedWebapp = distribution.getJettyBase().resolve("webapps").resolve("test"); + assertTrue(Files.exists(unpackedWebapp)); + Path webInf = unpackedWebapp.resolve("WEB-INF"); + assertTrue(Files.exists(webInf)); + Path quickstartWebXml = webInf.resolve("quickstart-web.xml"); + assertTrue(Files.exists(quickstartWebXml)); + assertNotEquals(0, Files.size(quickstartWebXml)); + + int port = distribution.freePort(); + + try (DistributionTester.Run run3 = distribution.start("jetty.http.port=" + port, "jetty.quickstart.mode=QUICKSTART")) + { + assertTrue(run3.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); + + startHttpClient(); + ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp"); + assertEquals(HttpStatus.OK_200, response.getStatus()); + assertThat(response.getContentAsString(), containsString("Hello")); + assertThat(response.getContentAsString(), not(containsString("<%"))); + } + } + } + } + @Test public void testSimpleWebAppWithJSP() throws Exception { @@ -100,7 +152,7 @@ public class DistributionTests extends AbstractDistributionTest int port = distribution.freePort(); try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp"); @@ -141,7 +193,7 @@ public class DistributionTests extends AbstractDistributionTest }; try (DistributionTester.Run run2 = distribution.start(args2)) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp"); @@ -177,7 +229,7 @@ public class DistributionTests extends AbstractDistributionTest int port = distribution.freePort(); try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); HTTP2Client h2Client = new HTTP2Client(); startHttpClient(() -> new HttpClient(new HttpClientTransportOverHTTP2(h2Client))); @@ -229,7 +281,7 @@ public class DistributionTests extends AbstractDistributionTest try (DistributionTester.Run run2 = distribution.start("jetty.unixsocket.path=" + sockFile.toString())) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(() -> new HttpClient(new HttpClientTransportOverUnixSockets(sockFile.toString()))); ContentResponse response = client.GET("http://localhost/test/index.jsp"); @@ -272,7 +324,7 @@ public class DistributionTests extends AbstractDistributionTest int port = distribution.freePort(); try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + port + "/test/index.jsp"); diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DynamicListenerTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DynamicListenerTests.java index c80ca9c0941..a16a5f475d2 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DynamicListenerTests.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/DynamicListenerTests.java @@ -78,7 +78,7 @@ public class DynamicListenerTests int port = distribution.freePort(); try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + port + "/test/testservlet/foo"); diff --git a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/OsgiAppTests.java b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/OsgiAppTests.java index 09cb1ef8478..c509d7e95d3 100644 --- a/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/OsgiAppTests.java +++ b/tests/test-distribution/src/test/java/org/eclipse/jetty/tests/distribution/OsgiAppTests.java @@ -57,7 +57,7 @@ public class OsgiAppTests extends AbstractDistributionTest int port = distribution.freePort(); try (DistributionTester.Run run2 = distribution.start("jetty.http.port=" + port)) { - assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS)); + assertTrue(run2.awaitConsoleLogsFor("Started Server@", 10, TimeUnit.SECONDS)); startHttpClient(); ContentResponse response = client.GET("http://localhost:" + port + "/test/info"); diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java new file mode 100644 index 00000000000..c569fe3d26d --- /dev/null +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/FailedSelectorTest.java @@ -0,0 +1,395 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.test; + +import java.io.IOException; +import java.net.URI; +import java.nio.channels.Selector; +import java.util.Collection; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; +import java.util.function.Function; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.HttpClientTransport; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.io.EndPoint; +import org.eclipse.jetty.io.ManagedSelector; +import org.eclipse.jetty.io.SelectorManager; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.DefaultHandler; +import org.eclipse.jetty.server.handler.HandlerList; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.log.StacklessLogging; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.util.thread.Scheduler; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class FailedSelectorTest +{ + private static final Logger LOG = Log.getLogger(FailedSelectorTest.class); + private HttpClient client; + private Server server; + private StacklessLogging stacklessManagedSelector; + + @AfterEach + public void stopServerAndClient() throws Exception + { + server.stop(); + client.stop(); + stacklessManagedSelector.close(); + } + + @BeforeEach + public void startClient() throws Exception + { + HttpClientTransport transport = new HttpClientTransportOverHTTP(1); + QueuedThreadPool qtp = new QueuedThreadPool(); + qtp.setName("Client"); + qtp.setStopTimeout(1000); + client = new HttpClient(transport); + client.setExecutor(qtp); + + client.setIdleTimeout(1000); + client.setMaxConnectionsPerDestination(1); + client.setMaxRequestsQueuedPerDestination(1); + client.start(); + } + + public void startServer(Function customizeServerConsumer) throws Exception + { + stacklessManagedSelector = new StacklessLogging(ManagedSelector.class); + + server = new Server(); + server.setStopTimeout(1000); + server.setStopAtShutdown(true); + + ServerConnector connector = customizeServerConsumer.apply(server); + server.addConnector(connector); + + ServletContextHandler context = new ServletContextHandler(); + context.setContextPath("/"); + context.addServlet(HelloServlet.class, "/hello"); + + ServletHolder closeHolder = new ServletHolder(new CloseSelectorServlet(connector)); + context.addServlet(closeHolder, "/selector/close"); + + HandlerList handlers = new HandlerList(); + handlers.addHandler(context); + handlers.addHandler(new DefaultHandler()); + + server.setHandler(handlers); + + server.start(); + } + + @Test + public void testRestartServerOnSelectFailure() throws Exception + { + CountDownLatch failedLatch = new CountDownLatch(1); + + startServer((server) -> + { + RestartSelectorCustomConnector connector = new RestartSelectorCustomConnector(server, 1, 1, new RestartServerTask(server, failedLatch)); + connector.setPort(0); + connector.setIdleTimeout(1000); + return connector; + }); + + // Request /hello + assertRequestHello(); + + // Request /selector/close + assertRequestSelectorClose(); + + // Wait for selectors to close from action above + assertTrue(failedLatch.await(2, TimeUnit.SECONDS)); + + // Request /hello + assertRequestHello(); + } + + @Test + public void testRestartSelectorOnSelectFailure() throws Exception + { + CountDownLatch failedLatch = new CountDownLatch(1); + + startServer((server) -> + { + RestartSelectorCustomConnector connector = new RestartSelectorCustomConnector(server, 1, 1, new RestartSelectorTask(failedLatch)); + connector.setPort(0); + connector.setIdleTimeout(1000); + return connector; + }); + + // Request /hello + assertRequestHello(); + + // Request /selector/close + assertRequestSelectorClose(); + + // Wait for selectors to close from action above + assertTrue(failedLatch.await(2, TimeUnit.SECONDS)); + + // Request /hello + assertRequestHello(); + } + + private void assertRequestSelectorClose() throws InterruptedException, ExecutionException, TimeoutException + { + URI dest = server.getURI().resolve("/selector/close"); + LOG.info("Requesting GET on {}", dest); + + ContentResponse response = client.newRequest(dest) + .method(HttpMethod.GET) + .header(HttpHeader.CONNECTION, "close") + .send(); + + assertThat(dest + " status", response.getStatus(), is(HttpStatus.OK_200)); + assertThat(dest + " response", response.getContentAsString(), startsWith("Closing selectors ")); + } + + private void assertRequestHello() throws InterruptedException, ExecutionException, TimeoutException + { + URI dest = server.getURI().resolve("/hello"); + LOG.info("Requesting GET on {}", dest); + ContentResponse response = client.newRequest(dest) + .method(HttpMethod.GET) + .header(HttpHeader.CONNECTION, "close") + .send(); + + assertThat(dest + " status", response.getStatus(), is(HttpStatus.OK_200)); + assertThat(dest + " response", response.getContentAsString(), startsWith("Hello ")); + } + + public static class HelloServlet extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException + { + resp.setContentType("text/plain"); + resp.setCharacterEncoding("utf-8"); + resp.getWriter().printf("Hello %s:%d%n", req.getRemoteAddr(), req.getRemotePort()); + } + } + + public static class CloseSelectorServlet extends HttpServlet + { + private static final int DELAY_MS = 500; + private ServerConnector connector; + private ScheduledExecutorService scheduledExecutorService; + + public CloseSelectorServlet(ServerConnector connector) + { + this.connector = connector; + scheduledExecutorService = Executors.newScheduledThreadPool(5); + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException + { + resp.setContentType("text/plain"); + resp.setCharacterEncoding("utf-8"); + resp.setHeader("Connection", "close"); + resp.getWriter().printf("Closing selectors in %,d ms%n", DELAY_MS); + scheduledExecutorService.schedule(new ForceCloseSelectorTask(connector), DELAY_MS, TimeUnit.MILLISECONDS); + } + } + + public static class RestartSelectorCustomConnector extends ServerConnector + { + private final Consumer onSelectFailConsumer; + + public RestartSelectorCustomConnector(Server server, int acceptors, int selectors, Consumer onSelectFailConsumer) + { + super(server, acceptors, selectors); + this.onSelectFailConsumer = onSelectFailConsumer; + } + + @Override + protected SelectorManager newSelectorManager(Executor executor, Scheduler scheduler, int selectors) + { + return new ServerConnectorManager(executor, scheduler, selectors) + { + @Override + protected ManagedSelector newSelector(int id) + { + return new CustomManagedSelector(this, id, onSelectFailConsumer); + } + }; + } + } + + public static class CustomManagedSelector extends ManagedSelector + { + private final Set endpoints = ConcurrentHashMap.newKeySet(); + private final Consumer onSelectFailConsumer; + + public CustomManagedSelector(SelectorManager selectorManager, int id, Consumer onSelectFailConsumer) + { + super(selectorManager, id); + this.onSelectFailConsumer = onSelectFailConsumer; + } + + @Override + protected void endPointOpened(EndPoint endPoint) + { + super.endPointOpened(endPoint); + endpoints.add(endPoint); + } + + @Override + protected void endPointClosed(EndPoint endPoint) + { + super.endPointClosed(endPoint); + endpoints.remove(endPoint); + } + + @Override + protected void onSelectFailed(Throwable cause) + { + endpoints.forEach((endpoint) -> + { + if (endpoint.getConnection() != null) + { + IO.close(endpoint.getConnection()); + } + IO.close(endpoint); + }); + endpoints.clear(); + + new Thread(() -> onSelectFailConsumer.accept(this), "OnSelectFailedTask").start(); + } + } + + private static class RestartSelectorTask implements Consumer + { + private static final Logger LOG = Log.getLogger(RestartSelectorTask.class); + private final CountDownLatch latch; + + public RestartSelectorTask(CountDownLatch latch) + { + this.latch = latch; + } + + @Override + public void accept(CustomManagedSelector customManagedSelector) + { + try + { + customManagedSelector.stop(); + customManagedSelector.start(); + } + catch (Exception e) + { + LOG.warn(e); + } + finally + { + latch.countDown(); + } + } + } + + private static class RestartServerTask implements Consumer + { + private static final Logger LOG = Log.getLogger(RestartServerTask.class); + private final Server server; + private final CountDownLatch latch; + + public RestartServerTask(Server server, CountDownLatch latch) + { + this.server = server; + this.latch = latch; + } + + @Override + public void accept(CustomManagedSelector customManagedSelector) + { + try + { + server.stop(); + server.start(); + } + catch (Exception e) + { + LOG.warn(e); + } + finally + { + latch.countDown(); + } + } + } + + private static class ForceCloseSelectorTask implements Runnable + { + private static final Logger LOG = Log.getLogger(ForceCloseSelectorTask.class); + private final ServerConnector connector; + + public ForceCloseSelectorTask(ServerConnector connector) + { + this.connector = connector; + } + + @Override + public void run() + { + SelectorManager selectorManager = connector.getSelectorManager(); + Collection managedSelectors = selectorManager.getBeans(ManagedSelector.class); + for (ManagedSelector managedSelector : managedSelectors) + { + if (managedSelector instanceof CustomManagedSelector) + { + CustomManagedSelector customManagedSelector = (CustomManagedSelector)managedSelector; + Selector selector = customManagedSelector.getSelector(); + LOG.debug("Closing selector {}}", selector); + IO.close(selector); + } + } + } + } +} \ No newline at end of file diff --git a/tests/test-integration/src/test/resources/jetty-logging.properties b/tests/test-integration/src/test/resources/jetty-logging.properties index 1531468c2c1..fdc5a51caba 100644 --- a/tests/test-integration/src/test/resources/jetty-logging.properties +++ b/tests/test-integration/src/test/resources/jetty-logging.properties @@ -1,3 +1,4 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog +org.eclipse.jetty.LEVEL=WARN #org.eclipse.jetty.LEVEL=DEBUG #org.eclipse.jetty.websocket.LEVEL=DEBUG diff --git a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartTest.java b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartTest.java index f1470a41104..c6f31c01639 100644 --- a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartTest.java +++ b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/QuickStartTest.java @@ -24,6 +24,9 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; +import org.eclipse.jetty.annotations.AnnotationConfiguration; +import org.eclipse.jetty.plus.webapp.EnvConfiguration; +import org.eclipse.jetty.plus.webapp.PlusConfiguration; import org.eclipse.jetty.server.NetworkConnector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; @@ -31,6 +34,7 @@ import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.resource.PathResource; import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebDescriptor; import org.eclipse.jetty.xml.XmlConfiguration; import org.eclipse.jetty.xml.XmlParser.Node; @@ -47,6 +51,7 @@ public class QuickStartTest @Test public void testStandardTestWar() throws Exception { + //Generate the quickstart PreconfigureStandardTestWar.main(new String[]{}); WebDescriptor descriptor = new WebDescriptor(Resource.newResource("./target/test-standard-preconfigured/WEB-INF/quickstart-web.xml")); @@ -65,8 +70,12 @@ public class QuickStartTest Server server = new Server(0); - QuickStartWebApp webapp = new QuickStartWebApp(); - webapp.setMode(QuickStartConfiguration.Mode.AUTO); + WebAppContext webapp = new WebAppContext(); + webapp.addConfiguration(new QuickStartConfiguration(), + new EnvConfiguration(), + new PlusConfiguration(), + new AnnotationConfiguration()); + webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART); webapp.setWar(war); webapp.setContextPath("/"); @@ -93,6 +102,7 @@ public class QuickStartTest @Test public void testSpecWar() throws Exception { + //Generate the quickstart xml PreconfigureSpecWar.main(new String[]{}); Path webXmlPath = MavenTestingUtils.getTargetPath().resolve("test-spec-preconfigured/WEB-INF/quickstart-web.xml"); @@ -114,8 +124,12 @@ public class QuickStartTest Server server = new Server(0); - QuickStartWebApp webapp = new QuickStartWebApp(); - webapp.setMode(QuickStartConfiguration.Mode.AUTO); + WebAppContext webapp = new WebAppContext(); + webapp.addConfiguration(new QuickStartConfiguration(), + new EnvConfiguration(), + new PlusConfiguration(), + new AnnotationConfiguration()); + webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART); webapp.setWar(war); webapp.setContextPath("/"); @@ -142,6 +156,7 @@ public class QuickStartTest @Test public void testJNDIWar() throws Exception { + //Generate the quickstart PreconfigureJNDIWar.main(new String[]{}); WebDescriptor descriptor = new WebDescriptor(Resource.newResource("./target/test-jndi-preconfigured/WEB-INF/quickstart-web.xml")); @@ -160,8 +175,12 @@ public class QuickStartTest Server server = new Server(0); - QuickStartWebApp webapp = new QuickStartWebApp(); - webapp.setMode(QuickStartConfiguration.Mode.AUTO); + WebAppContext webapp = new WebAppContext(); + webapp.addConfiguration(new QuickStartConfiguration(), + new EnvConfiguration(), + new PlusConfiguration(), + new AnnotationConfiguration()); + webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART); webapp.setWar(war); webapp.setContextPath("/"); diff --git a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/Quickstart.java b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/Quickstart.java index df47e4c038a..4f02f21ee4f 100644 --- a/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/Quickstart.java +++ b/tests/test-quickstart/src/test/java/org/eclipse/jetty/quickstart/Quickstart.java @@ -18,8 +18,12 @@ package org.eclipse.jetty.quickstart; +import org.eclipse.jetty.annotations.AnnotationConfiguration; +import org.eclipse.jetty.plus.webapp.EnvConfiguration; +import org.eclipse.jetty.plus.webapp.PlusConfiguration; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.xml.XmlConfiguration; public class Quickstart @@ -40,8 +44,12 @@ public class Quickstart Server server = new Server(8080); - QuickStartWebApp webapp = new QuickStartWebApp(); - webapp.setMode(QuickStartConfiguration.Mode.AUTO); + WebAppContext webapp = new WebAppContext(); + webapp.addConfiguration(new QuickStartConfiguration(), + new EnvConfiguration(), + new PlusConfiguration(), + new AnnotationConfiguration()); + webapp.setAttribute(QuickStartConfiguration.MODE, QuickStartConfiguration.Mode.QUICKSTART); webapp.setWar(war); webapp.setContextPath("/"); diff --git a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/AbstractSessionCacheTest.java b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/AbstractSessionCacheTest.java new file mode 100644 index 00000000000..c889b27bb85 --- /dev/null +++ b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/AbstractSessionCacheTest.java @@ -0,0 +1,513 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.server.session; + +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.TimeUnit; + +import javax.servlet.http.HttpSessionActivationListener; +import javax.servlet.http.HttpSessionEvent; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Base class for all tests on all flavours of SessionCache + * + */ +public abstract class AbstractSessionCacheTest +{ + + public static class TestSessionActivationListener implements HttpSessionActivationListener + { + public int passivateCalls = 0; + public int activateCalls = 0; + + @Override + public void sessionWillPassivate(HttpSessionEvent se) + { + ++passivateCalls; + } + + @Override + public void sessionDidActivate(HttpSessionEvent se) + { + ++activateCalls; + } + } + + public abstract AbstractSessionCacheFactory newSessionCacheFactory(int evictionPolicy, boolean saveOnCreate, + boolean saveOnInactiveEvict, boolean removeUnloadableSessions, + boolean flushOnResponseCommit); + + /** + * Test that a new Session object can be created from + * previously persisted data (SessionData). + */ + @Test + public void testNewSessionFromPersistedData() + throws Exception + { + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); + cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); + DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(true);//fake passivation + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + + context.start(); + + long now = System.currentTimeMillis(); + //fake persisted data + SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); + Session session = cache.newSession(data); + assertNotNull(session); + assertEquals("1234", session.getId()); + } + + + /** + * Test that the cache can load from the SessionDataStore + */ + @Test + public void testGetSessionNotInCache() + throws Exception + { + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + AbstractSessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); + SessionCache cache = cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + //put session data into the store + long now = System.currentTimeMillis(); + SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); + store.store("1234", data); + + assertFalse(cache.contains("1234")); + + Session session = cache.get("1234"); + assertEquals(1, session.getRequests()); + assertNotNull(session); + assertEquals("1234", session.getId()); + assertEquals(now - 20, session.getCreationTime()); + } + + @Test + public void testCommit() throws Exception + { + //Test state of session with call to commit + + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + //flushOnResponseCommit is true + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, true); + SessionCache cache = cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + //Mimic various states of a session when a response is about + //to be committed: + + //call commit: session has not changed, should not be written + store._numSaves.set(0); //clear save counter + Session session = createUnExpiredSession(cache, store, "1234"); + cache.add("1234", session); + session.getSessionData().setLastSaved(100);//simulate previously saved + commitAndCheckSaveState(cache, store, session, false, true, false, true, 0, 0); + + //call commit: session has changed, should be written + store._numSaves.set(0); //clear save counter + session = createUnExpiredSession(cache, store, "456"); + cache.add("456", session); + session.getSessionData().setLastSaved(100);//simulate previously saved + session.setAttribute("foo", "bar"); + commitAndCheckSaveState(cache, store, session, true, true, false, false, 0, 1); + + //call commit: only the metadata has changed will not be written + store._numSaves.set(0); //clear save counter + session = createUnExpiredSession(cache, store, "678"); + cache.add("678", session); + session.getSessionData().setLastSaved(100);//simulate previously saved + session.getSessionData().calcAndSetExpiry(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1)); + commitAndCheckSaveState(cache, store, session, false, true, false, true, 0, 0); + + //Test again with a savePeriod set - as savePeriod only + //affects saving when the session is not dirty, the savePeriod + //should not affect whether or not the session is saved on call + //to commit + store.setSavePeriodSec(60); + + //call commit: session has not changed, should not be written anyway + store._numSaves.set(0); //clear save counter + session = createUnExpiredSession(cache, store, "890"); + cache.add("890", session); + session.getSessionData().setLastSaved(100);//simulate previously saved + commitAndCheckSaveState(cache, store, session, false, true, false, true, 0, 0); + + //call commit: session has changed so session must be written + store._numSaves.set(0); //clear save counter + session = createUnExpiredSession(cache, store, "012"); + cache.add("012", session); + session.getSessionData().setLastSaved(100);//simulate previously saved + session.setAttribute("foo", "bar"); + commitAndCheckSaveState(cache, store, session, true, true, false, false, 0, 1); + + //call commit: only the metadata has changed will not be written + store._numSaves.set(0); //clear save counter + session = createUnExpiredSession(cache, store, "234"); + session.getSessionData().setMetaDataDirty(true); + cache.add("234", session); + session.getSessionData().setLastSaved(100);//simulate previously saved + session.getSessionData().calcAndSetExpiry(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1)); + commitAndCheckSaveState(cache, store, session, false, true, false, true, 0, 0); + } + + @Test + public void testCommitAndRelease() throws Exception + { + //test what happens with various states of a session when commit + //is called before release + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + //flushOnResponseCommit is true + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, true); + SessionCache cache = cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + //Mimic various states of a session when a response is about + //to be committed: + + //call commit: session has not changed, should not be written + Session session = createUnExpiredSession(cache, store, "1234"); + cache.add("1234", session); + commitAndCheckSaveState(cache, store, session, false, true, false, true, 0, 0); + //call release: session has not changed, but metadata has, should be written + cache.release("1234", session); + assertEquals(1, store._numSaves.get()); + assertFalse(session.getSessionData().isDirty()); + assertFalse(session.getSessionData().isMetaDataDirty()); + + //call commit: session has changed, should be written + store._numSaves.set(0); //clear save counter + session = createUnExpiredSession(cache, store, "456"); + cache.add("456", session); + session.setAttribute("foo", "bar"); + session.getSessionData().setLastSaved(100);//simulate not "new" session, ie has been previously saved + commitAndCheckSaveState(cache, store, session, true, true, false, false, 0, 1); + //call release: session not dirty but release changes metadata, so it will be saved + cache.release("456", session); + assertEquals(2, store._numSaves.get()); + assertFalse(session.getSessionData().isDirty()); + assertFalse(session.getSessionData().isMetaDataDirty()); + + //call commit: only the metadata has changed will not be written + store._numSaves.set(0); //clear save counter + session = createUnExpiredSession(cache, store, "678"); + session.getSessionData().calcAndSetExpiry(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1)); + session.getSessionData().setLastSaved(100); //simulate session not being "new", ie never previously saved + cache.add("678", session); + commitAndCheckSaveState(cache, store, session, false, true, false, true, 0, 0); + //call release: the metadata is dirty session should be written + cache.release("678", session); + assertEquals(1, store._numSaves.get()); + assertFalse(session.getSessionData().isDirty()); + assertFalse(session.getSessionData().isMetaDataDirty()); + + //Test again with a savePeriod set - only save if time last saved exceeds 60sec + store.setSavePeriodSec(60); + + //call commit: session has not changed, should not be written anyway + store._numSaves.set(0); //clear save counter + session = createUnExpiredSession(cache, store, "890"); + cache.add("890", session); + session.getSessionData().setLastSaved(100); //simulate last save long time ago + session.getSessionData().setMetaDataDirty(false); + commitAndCheckSaveState(cache, store, session, false, false, false, false, 0, 0); + //call release: not dirty but release sets metadata true, plus save period exceeded so write + cache.release("1234", session); + assertEquals(1, store._numSaves.get()); + assertFalse(session.getSessionData().isDirty()); + assertFalse(session.getSessionData().isMetaDataDirty()); + + //call commit: session has changed so session must be written + store._numSaves.set(0); //clear save counter + session = createUnExpiredSession(cache, store, "012"); + cache.add("012", session); + session.getSessionData().setLastSaved(100);//simulate previously saved session + session.setAttribute("foo", "bar"); + session.getSessionData().setMetaDataDirty(false); + commitAndCheckSaveState(cache, store, session, true, false, false, false, 0, 1); + //call release: not dirty, release sets metadirty true (recalc expiry) but previous save too recent to exceed save period --> no write + cache.release("012", session); + assertEquals(1, store._numSaves.get()); + assertFalse(session.getSessionData().isDirty()); + assertTrue(session.getSessionData().isMetaDataDirty()); + + //call commit: only the metadata has changed will not be written + store._numSaves.set(0); //clear save counter + session = createUnExpiredSession(cache, store, "234"); + session.getSessionData().calcAndSetExpiry(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1)); + session.getSessionData().setLastSaved(System.currentTimeMillis());//simulate session last saved recently + commitAndCheckSaveState(cache, store, session, false, true, false, true, 0, 0); + //call release: not dirty, release sets metadirty true (recalc expiry) but not within saveperiod so skip write + cache.release("1234", session); + assertEquals(0, store._numSaves.get()); + assertFalse(session.getSessionData().isDirty()); + assertTrue(session.getSessionData().isMetaDataDirty()); + } + + /** + * Test the exist method. + */ + @Test + public void testExists() + throws Exception + { + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); + SessionCache cache = (SessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + //test one that doesn't exist at all + assertFalse(cache.exists("1234")); + + //test one that only exists in the store + long now = System.currentTimeMillis(); + SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); + store.store("1234", data); + assertTrue(cache.exists("1234")); + + //test one that exists in the cache also + Session session = cache.newSession(data); + cache.add("1234", session); + assertTrue(cache.exists("1234")); + } + + /** + * Test the delete method. + */ + @Test + public void testDelete() + throws Exception + { + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, true, false, false, false); + SessionCache cache = cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + //test remove non-existent session + Session session = cache.delete("1234"); + assertNull(session); + + //test remove of existing session in store only + long now = System.currentTimeMillis(); + SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); + store.store("1234", data); + session = cache.delete("1234"); + assertNotNull(session); + assertFalse(store.exists("1234")); + assertFalse(cache.contains("1234")); + + //test remove of session in both store and cache + session = cache.newSession(null, "1234",now - 20, TimeUnit.MINUTES.toMillis(10));//saveOnCreate ensures write to store + cache.add("1234", session); + assertTrue(store.exists("1234")); + assertTrue(cache.contains("1234")); + session = cache.delete("1234"); + assertNotNull(session); + assertFalse(store.exists("1234")); + assertFalse(cache.contains("1234")); + } + + @Test + public void testExpiration() + throws Exception + { + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); + SessionCache cache = cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + //test no candidates, no data in store + Set result = cache.checkExpiration(Collections.emptySet()); + assertTrue(result.isEmpty()); + + //test candidates that are in the cache and NOT expired + long now = System.currentTimeMillis(); + SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); + data.setExpiry(now + TimeUnit.DAYS.toMillis(1)); + Session session = cache.newSession(data); + cache.add("1234", session); + cache.release("1234", session); + assertTrue(cache.exists("1234")); + result = cache.checkExpiration(Collections.singleton("1234")); + assertTrue(result.isEmpty()); + + //test candidates that are in the cache AND expired + data.setExpiry(1); + result = cache.checkExpiration(Collections.singleton("1234")); + assertEquals(1, result.size()); + assertEquals("1234", result.iterator().next()); + + //test candidates that are not in the cache + SessionData data2 = store.newSessionData("567", now - 50, now - 40, now - 30, TimeUnit.MINUTES.toMillis(10)); + data2.setExpiry(1); + store.store("567", data2); + + result = cache.checkExpiration(Collections.emptySet()); + assertThat(result, containsInAnyOrder("1234", "567")); + } + + @Test + public void testSaveOnCreateTrue() + throws Exception + { + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, true, false, false, false); + SessionCache cache = cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + long now = System.currentTimeMillis(); + cache.newSession(null, "1234", now, TimeUnit.MINUTES.toMillis(10)); + assertTrue(store.exists("1234")); + } + + @Test + public void testSaveOnCreateFalse() + throws Exception + { + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); + SessionCache cache = cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + long now = System.currentTimeMillis(); + cache.newSession(null, "1234", now, TimeUnit.MINUTES.toMillis(10)); + assertFalse(store.exists("1234")); + } + + public void commitAndCheckSaveState(SessionCache cache, TestSessionDataStore store, Session session, + boolean expectedBeforeDirty, boolean expectedBeforeMetaDirty, + boolean expectedAfterDirty, boolean expectedAfterMetaDirty, + int expectedBeforeNumSaves, int expectedAfterNumSaves) + throws Exception + { + assertEquals(expectedBeforeDirty, session.getSessionData().isDirty()); + assertEquals(expectedBeforeMetaDirty, session.getSessionData().isMetaDataDirty()); + assertEquals(expectedBeforeNumSaves, store._numSaves.get()); + cache.commit(session); + assertEquals(expectedAfterDirty, session.getSessionData().isDirty()); + assertEquals(expectedAfterMetaDirty, session.getSessionData().isMetaDataDirty()); + assertEquals(expectedAfterNumSaves, store._numSaves.get()); + } + + public Session createUnExpiredSession(SessionCache cache, SessionDataStore store, String id) + { + long now = System.currentTimeMillis(); + SessionData data = store.newSessionData(id, now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); + data.setExpiry(now + TimeUnit.DAYS.toMillis(1)); + return cache.newSession(data); + } +} diff --git a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/DefaultSessionCacheTest.java b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/DefaultSessionCacheTest.java index 21b9bc44b83..46a812a5976 100644 --- a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/DefaultSessionCacheTest.java +++ b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/DefaultSessionCacheTest.java @@ -18,25 +18,19 @@ package org.eclipse.jetty.server.session; -import java.util.Collections; import java.util.Random; -import java.util.Set; import java.util.concurrent.TimeUnit; + import javax.servlet.http.HttpSession; -import javax.servlet.http.HttpSessionActivationListener; -import javax.servlet.http.HttpSessionEvent; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.junit.jupiter.api.Test; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.containsInAnyOrder; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -44,25 +38,21 @@ import static org.junit.jupiter.api.Assertions.fail; /** * DefaultSessionCacheTest */ -public class DefaultSessionCacheTest +public class DefaultSessionCacheTest extends AbstractSessionCacheTest { - public static class TestSessionActivationListener implements HttpSessionActivationListener + @Override + public AbstractSessionCacheFactory newSessionCacheFactory(int evictionPolicy, boolean saveOnCreate, + boolean saveOnInactiveEvict, boolean removeUnloadableSessions, + boolean flushOnResponseCommit) { - public int passivateCalls = 0; - public int activateCalls = 0; - - @Override - public void sessionWillPassivate(HttpSessionEvent se) - { - ++passivateCalls; - } - - @Override - public void sessionDidActivate(HttpSessionEvent se) - { - ++activateCalls; - } + DefaultSessionCacheFactory factory = new DefaultSessionCacheFactory(); + factory.setEvictionPolicy(evictionPolicy); + factory.setSaveOnCreate(saveOnCreate); + factory.setSaveOnInactiveEvict(saveOnInactiveEvict); + factory.setRemoveUnloadableSessions(removeUnloadableSessions); + factory.setFlushOnResponseCommit(flushOnResponseCommit); + return factory; } @Test @@ -72,9 +62,8 @@ public class DefaultSessionCacheTest int inactivePeriod = 20; int scavengePeriod = 3; - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setSaveOnCreate(true); //ensures that a session is persisted as soon as it is created - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); + AbstractSessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); + SessionDataStoreFactory storeFactory = new TestSessionDataStoreFactory(); TestServer server = new TestServer(0, inactivePeriod, scavengePeriod, cacheFactory, storeFactory); ServletContextHandler contextHandler = server.addContext("/test"); @@ -203,8 +192,7 @@ public class DefaultSessionCacheTest context.setContextPath("/test"); context.setServer(server); - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); + AbstractSessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); TestSessionDataStore store = new TestSessionDataStore(true);//fake passivation @@ -234,38 +222,6 @@ public class DefaultSessionCacheTest assertEquals(1, listener.activateCalls); } - /** - * Test that a new Session object can be created from - * previously persisted data (SessionData). - */ - @Test - public void testNewSessionFromPersistedData() - throws Exception - { - Server server = new Server(); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); - context.setServer(server); - - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); - DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(true);//fake passivation - cache.setSessionDataStore(store); - context.getSessionHandler().setSessionCache(cache); - - context.start(); - - long now = System.currentTimeMillis(); - //fake persisted data - SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); - Session session = cache.newSession(data); - assertNotNull(session); - assertEquals("1234", session.getId()); - } - /** * Test that a session id can be renewed. */ @@ -318,8 +274,7 @@ public class DefaultSessionCacheTest context.setContextPath("/test"); context.setServer(server); - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); + AbstractSessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); TestSessionDataStore store = new TestSessionDataStore(); @@ -338,42 +293,6 @@ public class DefaultSessionCacheTest assertTrue(((AbstractSessionCache)cache).contains("1234")); } - /** - * Test that the cache can load from the SessionDataStore - */ - @Test - public void testGetSessionNotInCache() - throws Exception - { - Server server = new Server(); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); - context.setServer(server); - - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); - DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(); - cache.setSessionDataStore(store); - context.getSessionHandler().setSessionCache(cache); - context.start(); - - //put session data into the store - long now = System.currentTimeMillis(); - SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); - store.store("1234", data); - - assertFalse(cache.contains("1234")); - - Session session = cache.get("1234"); - assertEquals(1, session.getRequests()); - assertNotNull(session); - assertEquals("1234", session.getId()); - assertEquals(now - 20, session.getCreationTime()); - } - @Test public void testAdd() throws Exception @@ -384,8 +303,7 @@ public class DefaultSessionCacheTest context.setContextPath("/test"); context.setServer(server); - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); + AbstractSessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); TestSessionDataStore store = new TestSessionDataStore(); @@ -418,8 +336,7 @@ public class DefaultSessionCacheTest context.setContextPath("/test"); context.setServer(server); - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); TestSessionDataStore store = new TestSessionDataStore(); @@ -458,9 +375,8 @@ public class DefaultSessionCacheTest context.setContextPath("/test"); context.setServer(server); - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); - DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); + SessionCache cache = (SessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); TestSessionDataStore store = new TestSessionDataStore(); cache.setSessionDataStore(store); @@ -478,139 +394,6 @@ public class DefaultSessionCacheTest assertTrue(cache.contains("1234")); } - /** - * Test the exist method. - */ - @Test - public void testExists() - throws Exception - { - Server server = new Server(); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); - context.setServer(server); - - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); - DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(); - cache.setSessionDataStore(store); - context.getSessionHandler().setSessionCache(cache); - context.start(); - - //test one that doesn't exist at all - assertFalse(cache.exists("1234")); - - //test one that only exists in the store - long now = System.currentTimeMillis(); - SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); - store.store("1234", data); - assertTrue(cache.exists("1234")); - - //test one that exists in the cache also - Session session = cache.newSession(data); - cache.add("1234", session); - assertTrue(cache.exists("1234")); - } - - /** - * Test the delete method. - */ - @Test - public void testDelete() - throws Exception - { - Server server = new Server(); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); - context.setServer(server); - - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); - cacheFactory.setSaveOnCreate(true); - DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(); - cache.setSessionDataStore(store); - context.getSessionHandler().setSessionCache(cache); - context.start(); - - //test remove non-existent session - Session session = cache.delete("1234"); - assertNull(session); - - //test remove of existing session in store only - long now = System.currentTimeMillis(); - SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); - store.store("1234", data); - session = cache.delete("1234"); - assertNotNull(session); - assertFalse(store.exists("1234")); - assertFalse(cache.contains("1234")); - - //test remove of session in both store and cache - session = cache.newSession(null, "1234",now - 20, TimeUnit.MINUTES.toMillis(10));//saveOnCreate ensures write to store - cache.add("1234", session); - assertTrue(store.exists("1234")); - assertTrue(cache.contains("1234")); - session = cache.delete("1234"); - assertNotNull(session); - assertFalse(store.exists("1234")); - assertFalse(cache.contains("1234")); - } - - @Test - public void testExpiration() - throws Exception - { - Server server = new Server(); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); - context.setServer(server); - - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); - DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(); - cache.setSessionDataStore(store); - context.getSessionHandler().setSessionCache(cache); - context.start(); - - //test no candidates, no data in store - Set result = cache.checkExpiration(Collections.emptySet()); - assertTrue(result.isEmpty()); - - //test candidates that are in the cache and NOT expired - long now = System.currentTimeMillis(); - SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); - data.setExpiry(now + TimeUnit.DAYS.toMillis(1)); - Session session = cache.newSession(data); - cache.add("1234", session); - cache.release("1234", session); - assertTrue(cache.exists("1234")); - result = cache.checkExpiration(Collections.singleton("1234")); - assertTrue(result.isEmpty()); - - //test candidates that are in the cache AND expired - data.setExpiry(1); - result = cache.checkExpiration(Collections.singleton("1234")); - assertEquals(1, result.size()); - assertEquals("1234", result.iterator().next()); - - //test candidates that are not in the cache - SessionData data2 = store.newSessionData("567", now - 50, now - 40, now - 30, TimeUnit.MINUTES.toMillis(10)); - data2.setExpiry(1); - store.store("567", data2); - - result = cache.checkExpiration(Collections.emptySet()); - assertThat(result, containsInAnyOrder("1234", "567")); - } - @Test public void testCheckInactiveSession() throws Exception @@ -725,54 +508,4 @@ public class DefaultSessionCacheTest SessionData retrieved = store.load("1234"); assertEquals(accessed, retrieved.getAccessed()); //check that we persisted the session before we evicted } - - @Test - public void testSaveOnCreateTrue() - throws Exception - { - Server server = new Server(); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); - context.setServer(server); - - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); - cacheFactory.setSaveOnCreate(true); - DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(); - cache.setSessionDataStore(store); - context.getSessionHandler().setSessionCache(cache); - context.start(); - - long now = System.currentTimeMillis(); - cache.newSession(null, "1234", now, TimeUnit.MINUTES.toMillis(10)); - assertTrue(store.exists("1234")); - } - - @Test - public void testSaveOnCreateFalse() - throws Exception - { - Server server = new Server(); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); - context.setServer(server); - - DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); - cacheFactory.setEvictionPolicy(SessionCache.NEVER_EVICT); - cacheFactory.setSaveOnCreate(false); - DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(); - cache.setSessionDataStore(store); - context.getSessionHandler().setSessionCache(cache); - context.start(); - - long now = System.currentTimeMillis(); - cache.newSession(null, "1234", now, TimeUnit.MINUTES.toMillis(10)); - assertFalse(store.exists("1234")); - } } diff --git a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/NullSessionCacheTest.java b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/NullSessionCacheTest.java index 5e63b62101b..6dce6c5e0ef 100644 --- a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/NullSessionCacheTest.java +++ b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/NullSessionCacheTest.java @@ -18,129 +18,37 @@ package org.eclipse.jetty.server.session; -import java.io.Serializable; import java.util.concurrent.TimeUnit; -import javax.servlet.http.HttpSessionActivationListener; -import javax.servlet.http.HttpSessionEvent; - import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; /** * NullSessionCacheTest */ -public class NullSessionCacheTest -{ - public static class SerializableTestObject implements Serializable, HttpSessionActivationListener +public class NullSessionCacheTest extends AbstractSessionCacheTest +{ + @Override + public AbstractSessionCacheFactory newSessionCacheFactory(int evictionPolicy, boolean saveOnCreate, + boolean saveOnInactiveEvict, boolean removeUnloadableSessions, + boolean flushOnResponseCommit) { - int count; - static int passivates = 0; - static int activates = 0; - - public SerializableTestObject(int i) - { - count = i; - } - - @Override - public void sessionWillPassivate(HttpSessionEvent se) - { - //should never be called, as we are replaced with the - //non-serializable object and thus passivate will be called on that - ++passivates; - } - - @Override - public void sessionDidActivate(HttpSessionEvent se) - { - ++activates; - //remove myself, replace with something serializable - se.getSession().setAttribute("pv", new TestObject(count)); - } - } - - public static class TestObject implements HttpSessionActivationListener - { - int i; - static int passivates = 0; - static int activates = 0; - - public TestObject(int j) - { - i = j; - } - - @Override - public void sessionWillPassivate(HttpSessionEvent se) - { - ++passivates; - //remove myself, replace with something serializable - se.getSession().setAttribute("pv", new SerializableTestObject(i)); - } - - @Override - public void sessionDidActivate(HttpSessionEvent se) - { - //this should never be called because we replace ourselves during passivation, - //so it is the SerializableTestObject that is activated instead - ++activates; - } - } - - @Test - public void testWritesWithPassivation() throws Exception - { - //Test that a session that is in the process of being saved cannot cause - //another save via a passivation listener - Server server = new Server(); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); - context.setServer(server); - - NullSessionCacheFactory cacheFactory = new NullSessionCacheFactory(); - cacheFactory.setWriteThroughMode(NullSessionCache.WriteThroughMode.ALWAYS); - - NullSessionCache cache = (NullSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(true); //pretend to passivate - cache.setSessionDataStore(store); - context.getSessionHandler().setSessionCache(cache); - - context.start(); - - //make a session - long now = System.currentTimeMillis(); - SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); - data.setExpiry(now + TimeUnit.DAYS.toMillis(1)); - Session session = cache.newSession(null, data); //mimic a request making a session - cache.add("1234", session); - //at this point the session should not be saved to the store - assertEquals(0, store._numSaves.get()); - - //set an attribute that is not serializable, should cause a save - TestObject obj = new TestObject(1); - session.setAttribute("pv", obj); - assertTrue(cache._listener._sessionsBeingWritten.isEmpty()); - assertTrue(store.exists("1234")); - assertEquals(1, store._numSaves.get()); - assertEquals(1, TestObject.passivates); - assertEquals(0, TestObject.activates); - assertEquals(1, SerializableTestObject.activates); - assertEquals(0, SerializableTestObject.passivates); + NullSessionCacheFactory factory = new NullSessionCacheFactory(); + factory.setSaveOnCreate(saveOnCreate); + factory.setRemoveUnloadableSessions(removeUnloadableSessions); + factory.setFlushOnResponseCommit(flushOnResponseCommit); + return factory; } @Test - public void testChangeWriteThroughMode() throws Exception + public void testShutdownWithSessionStore() + throws Exception { Server server = new Server(); @@ -148,157 +56,45 @@ public class NullSessionCacheTest context.setContextPath("/test"); context.setServer(server); - NullSessionCacheFactory cacheFactory = new NullSessionCacheFactory(); + AbstractSessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); + SessionCache cache = cacheFactory.getSessionCache(context.getSessionHandler()); - NullSessionCache cache = (NullSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(); + TestSessionDataStore store = new TestSessionDataStore(true);//fake passivation cache.setSessionDataStore(store); context.getSessionHandler().setSessionCache(cache); - - assertEquals(NullSessionCache.WriteThroughMode.ON_EXIT, cache.getWriteThroughMode()); - assertNull(cache._listener); - - //change mode to NEW - cache.setWriteThroughMode(NullSessionCache.WriteThroughMode.NEW); - assertEquals(NullSessionCache.WriteThroughMode.NEW, cache.getWriteThroughMode()); - assertNotNull(cache._listener); - assertEquals(1, context.getSessionHandler()._sessionAttributeListeners.size()); - assertTrue(context.getSessionHandler()._sessionAttributeListeners.contains(cache._listener)); - - //change mode to ALWAYS from NEW, listener should remain - NullSessionCache.WriteThroughAttributeListener old = cache._listener; - cache.setWriteThroughMode(NullSessionCache.WriteThroughMode.ALWAYS); - assertEquals(NullSessionCache.WriteThroughMode.ALWAYS, cache.getWriteThroughMode()); - assertNotNull(cache._listener); - assertSame(old,cache._listener); - assertEquals(1, context.getSessionHandler()._sessionAttributeListeners.size()); - - //check null is same as ON_EXIT - cache.setWriteThroughMode(null); - assertEquals(NullSessionCache.WriteThroughMode.ON_EXIT, cache.getWriteThroughMode()); - assertNull(cache._listener); - assertEquals(0, context.getSessionHandler()._sessionAttributeListeners.size()); - - //change to ON_EXIT - cache.setWriteThroughMode(NullSessionCache.WriteThroughMode.ON_EXIT); - assertEquals(NullSessionCache.WriteThroughMode.ON_EXIT, cache.getWriteThroughMode()); - assertNull(cache._listener); - assertEquals(0, context.getSessionHandler()._sessionAttributeListeners.size()); - } - - @Test - public void testWriteThroughAlways() throws Exception - { - Server server = new Server(); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); - context.setServer(server); - - NullSessionCacheFactory cacheFactory = new NullSessionCacheFactory(); - cacheFactory.setWriteThroughMode(NullSessionCache.WriteThroughMode.ALWAYS); - - NullSessionCache cache = (NullSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(); - cache.setSessionDataStore(store); - context.getSessionHandler().setSessionCache(cache); context.start(); - //make a session + //put a session in the cache and store long now = System.currentTimeMillis(); SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); - data.setExpiry(now + TimeUnit.DAYS.toMillis(1)); - Session session = cache.newSession(null, data); //mimic a request making a session + Session session = cache.newSession(data); + TestSessionActivationListener listener = new TestSessionActivationListener(); cache.add("1234", session); - //at this point the session should not be saved to the store - assertEquals(0, store._numSaves.get()); - - //check each call to set attribute results in a store - session.setAttribute("colour", "blue"); - assertTrue(store.exists("1234")); - assertEquals(1, store._numSaves.get()); - - //mimic releasing the session after the request is finished - cache.release("1234", session); - assertTrue(store.exists("1234")); + //cache never contains the session assertFalse(cache.contains("1234")); - assertEquals(2, store._numSaves.get()); - - //simulate a new request using the previously created session - //the session should not now be new - session = cache.get("1234"); //get the session again - session.access(now); //simulate a request - session.setAttribute("spin", "left"); - assertTrue(store.exists("1234")); - assertEquals(3, store._numSaves.get()); - cache.release("1234", session); //finish with the session + session.setAttribute("aaa", listener); + //write session out on release + cache.release("1234", session); + assertEquals(1, store._numSaves.get()); + assertEquals(1, listener.passivateCalls); + assertEquals(0, listener.activateCalls); //NullSessionCache always evicts on release, so never reactivates - assertFalse(session.isResident()); + assertTrue(store.exists("1234")); + //cache never contains session + assertFalse(cache.contains("1234")); + + context.stop(); //calls shutdown + + //session should still exist in store + assertTrue(store.exists("1234")); + //cache never contains the session + assertFalse(cache.contains("1234")); + //shutdown does not save session + assertEquals(1, listener.passivateCalls); + assertEquals(0, listener.activateCalls); } - @Test - public void testWriteThroughNew() throws Exception - { - Server server = new Server(); - - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); - context.setContextPath("/test"); - context.setServer(server); - - NullSessionCacheFactory cacheFactory = new NullSessionCacheFactory(); - cacheFactory.setWriteThroughMode(NullSessionCache.WriteThroughMode.NEW); - - NullSessionCache cache = (NullSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); - - TestSessionDataStore store = new TestSessionDataStore(); - cache.setSessionDataStore(store); - context.getSessionHandler().setSessionCache(cache); - context.start(); - - //make a session - long now = System.currentTimeMillis(); - SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); - data.setExpiry(now + TimeUnit.DAYS.toMillis(1)); - Session session = cache.newSession(null, data); //mimic a request making a session - cache.add("1234", session); - //at this point the session should not be saved to the store - assertEquals(0, store._numSaves.get()); - assertTrue(session.isNew()); - - //check each call to set attribute results in a store while the session is new - session.setAttribute("colour", "blue"); - assertTrue(store.exists("1234")); - assertEquals(1, store._numSaves.get()); - session.setAttribute("charge", "positive"); - assertEquals(2, store._numSaves.get()); - - //mimic releasing the session after the request is finished - cache.release("1234", session); - assertTrue(store.exists("1234")); - assertFalse(cache.contains("1234")); - assertEquals(3, store._numSaves.get()); //even if the session isn't dirty, we will save the access time - - - //simulate a new request using the previously created session - //the session should not now be new, so setAttribute should - //not result in a save - session = cache.get("1234"); //get the session again - session.access(now); //simulate a request - assertFalse(session.isNew()); - assertEquals(3, store._numSaves.get()); - session.setAttribute("spin", "left"); - assertTrue(store.exists("1234")); - assertEquals(3, store._numSaves.get()); - session.setAttribute("flavor", "charm"); - assertEquals(3, store._numSaves.get()); - cache.release("1234", session); //finish with the session - assertEquals(4, store._numSaves.get());//release session should write it out - assertFalse(session.isResident()); - } - @Test public void testNotCached() throws Exception { @@ -324,7 +120,7 @@ public class NullSessionCacheTest data.setExpiry(now + TimeUnit.DAYS.toMillis(1)); Session session = cache.newSession(null, data); //mimic a request making a session cache.add("1234", session); - assertFalse(cache.contains("1234"));//null cache doesn't actually store the session + assertFalse(cache.contains("1234"));//null cache doesn't actually retain the session //mimic releasing the session after the request is finished cache.release("1234", session); @@ -336,7 +132,104 @@ public class NullSessionCacheTest session.access(now); //simulate a request cache.release("1234", session); //finish with the session assertFalse(cache.contains("1234")); - assertFalse(session.isResident()); } + + /** + * Test contains method. + */ + @Test + public void testContains() + throws Exception + { + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); + SessionCache cache = (SessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + //test one that doesn't exist + assertFalse(cache.contains("1234")); + + //test one that exists + long now = System.currentTimeMillis(); + SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); + Session session = cache.newSession(data); + cache.add("1234", session); + assertFalse(cache.contains("1234")); + } + + /** + * Test the exist method. + */ + @Test + public void testExists() + throws Exception + { + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, false, false, false, false); + SessionCache cache = (SessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + //test one that doesn't exist anywhere at all + assertFalse(cache.exists("1234")); + + //test one that only exists in the store + long now = System.currentTimeMillis(); + SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); + store.store("1234", data); + assertTrue(cache.exists("1234")); + } + + /** + * Test the delete method. + */ + @Test + public void testDelete() + throws Exception + { + Server server = new Server(); + + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + + SessionCacheFactory cacheFactory = newSessionCacheFactory(SessionCache.NEVER_EVICT, true, false, false, false); + SessionCache cache = cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + context.start(); + + //test remove non-existent session + Session session = cache.delete("1234"); + assertNull(session); + + //test remove of existing session in store only + long now = System.currentTimeMillis(); + SessionData data = store.newSessionData("1234", now - 20, now - 10, now - 20, TimeUnit.MINUTES.toMillis(10)); + store.store("1234", data); + session = cache.delete("1234"); + assertNull(session); //NullSessionCache never returns the session that was removed from the cache because it was never in the cache! + assertFalse(store.exists("1234")); + assertFalse(cache.contains("1234")); + } } diff --git a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionEvictionFailureTest.java b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionEvictionFailureTest.java index d6eac23dc6f..237d74bd18e 100644 --- a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionEvictionFailureTest.java +++ b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionEvictionFailureTest.java @@ -37,6 +37,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * SessionEvictionFailureTest @@ -192,6 +193,7 @@ public class SessionEvictionFailureTest // Make another request to see if the session is still in the cache and can be used, //allow it to be saved this time + assertTrue(context.getSessionHandler().getSessionCache().contains(TestServer.extractSessionId(sessionCookie))); Request request = client.newRequest(url + "?action=test"); response = request.send(); assertEquals(HttpServletResponse.SC_OK, response.getStatus()); diff --git a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/test/AnnotationTest.java b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/test/AnnotationTest.java index 0aead59d67c..e7a99fa2fd8 100644 --- a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/test/AnnotationTest.java +++ b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/test/AnnotationTest.java @@ -295,19 +295,19 @@ public class AnnotationTest extends HttpServlet out.println("private Double minAmount;"); out.println(""); if (maxAmount == null) - out.println("

Result: " + envResult + ": FAIL"); + out.println("

Result: " + envResult + ": FAIL"); else out.println("

Result: " + envResult + ": " + (maxAmount.compareTo(55D) == 0 ? " PASS" : " FAIL") + ""); out.println("
JNDI Lookup Result: " + envLookupResult + ""); if (minAmount == null) - out.println("

Result: " + envResult2 + ": FAIL"); + out.println("

Result: " + envResult2 + ": FAIL"); else out.println("
Result: " + envResult2 + ": " + (minAmount.compareTo(0.99D) == 0 ? " PASS" : " FAIL") + ""); out.println("
JNDI Lookup Result: " + envLookupResult2 + ""); if (avgAmount == null) - out.println("

Result: " + envResult3 + ": FAIL"); + out.println("

Result: " + envResult3 + ": FAIL"); else out.println("
Result: " + envResult3 + ": " + (avgAmount.compareTo(1.25D) == 0 ? " PASS" : " FAIL") + ""); out.println("
JNDI Lookup Result: " + envLookupResult3 + "

");