diff --git a/VERSION.txt b/VERSION.txt
index aa06e5e5d2e..660c2d92606 100644
--- a/VERSION.txt
+++ b/VERSION.txt
@@ -1,5 +1,101 @@
jetty-10.0.0-SNAPSHOT
+jetty-9.4.7.v20170914 - 14 September 2017
+ + 215 Consider native ALPN/SSL provider
+ + 487 JDK 9 build compatibility
+ + 1116 Support empty HTTP header values
+ + 1200 Use PathWatcher in DeploymentManager
+ + 1357 RolloverFileOutputStream: No rollout performed at midnight
+ + 1416 GzipHandler generated ETag suffix has problems with If-Match header
+ logic
+ + 1468 Configure PKIX Revocation Checker for SslContextFactory
+ + 1469 RolloverFileOutputStream: IllegalStateException Task already scheduled
+ + 1498 Add JRTResource to support future Java 9 classloader behaviors
+ + 1499 ClasspathPattern needs MODULE ruleset to support future Java 9
+ classloader behaviors
+ + 1503 IPv6 address needs normalization (without brackets) in
+ ForwardedRequestCustomizer
+ + 1507 RolloverFileOutputStream: Negative delay Timer.schedule exception
+ + 1513 RolloverFileOutputStream: can't handle multiple instances
+ + 1515 Improved RollOverFileOutputStream removeOldFiles() behavior
+ + 1520 PropertyUserStore should extract packed config file
+ + 1556 Remove a timing channel in Password matching
+ + 1571 Support Hazelcast session management in 9.4
+ + 1590 Improve RolloverFileOutputStream functionality with multiple TimeZones
+ + 1591 JDBCSessionDataStore doesn't work with root context on Oracle DB
+ + 1592 CompressedContentFormat.tagEquals() - incorrect comparison of entity
+ tag hashes
+ + 1595 HTTP/2: Avoid sending unnecessary stream WINDOW_UPDATE frames
+ + 1600 Update jndi.mod and plus.mod
+ + 1603 WebSocketServerFactory NPE in toString()
+ + 1604 WebSocketContainer stop needs improvement
+ + 1605 ContainerProvider.getWebSocketContainer() behavior is not to spec
+ + 1618 AsyncContext.dispatch() does not use raw/encoded URI
+ + 1622 HeaderFilter doesn't work if the response has been committed
+ + 1623 JettyRunMojo use dependencies from reactor (outputdirectory)
+ + 1625 Support new IANA declared Websocket Close Status Codes
+ + 1637 Thread per connection retained in HTTP/2
+ + 1638 Add it test for Maven Plugin
+ + 1642 Using RewriteHandler with AsyncContext.dispatch() and
+ HttpServletRequestWrapper not possible
+ + 1643 ProxyServlet always uses default number of selector threads -
+ constructor should allow to overwrite the default.
+ + 1645 NotSerializableException: DoSFilter when using Non-Clustered Session
+ Management: File System
+ + 1655 Improve extensibility of ServerConnector
+ + 1656 Improve configurability of ConnectionPools
+ + 1661 AbstractProxyServlet onProxyResponseFailure Error
+ + 1662 NPE with WebSocket Compress Extensions
+ + 1664 IPAccessHandler CIDR IP range check is incorrect
+ + 1671 Asymmetric usage of trailers in MetaData.Request
+ + 1675 Session id should not be logged with INFO level in AbstractSessionCache
+ + 1679 DeploymentManagerMBean not usable through JMX
+ + 1682 Jetty-WarFragmentFolderPath directive has no effect in eclipse runtime
+ mode except for the first launch
+ + 1685 Update ALPN support for Java 8u141
+ + 1687 HTTP2: Correcting missing callback notification when channel not found
+ + 1692 Annotation scanning should ignore `module-info.class` files
+ + 1698 Missing WWW-Authenticate from SpnegoAuthenticator when other
+ Authorization header provided
+ + 1702 Update ALPN support for Java 8u144
+ + 1703 Improve HttpInput failure logging
+ + 1706 Log Implementation ignored when executing under OSGi
+ + 1709 SpnegoAuthenticator improperly handling case-insensitive Negotiate
+ header
+ + 1713 Do not over allocate selectors for small thread pools
+ + 1715 Standardise properties and ids in jetty XML files
+ + 1717 DoSFilter getRateTracker IP/Port loadId minor improvement
+ + 1718 QueuedThreadPool not exposed on JMX
+ + 1719 HTTP/2: Improve handling of queued requests
+ + 1721 Async I/O POST fails with big files
+ + 1724 Add dependency on jetty-annotations for apache-jsp
+ + 1732 Allow pause accepting new connections during high load
+ + 1737 DefaultServlet wrong welcome dispatcher using non-root URL path
+ + 1738 jetty-bom fails oss.sonatype.org validation
+ + 1741 Java 9 javadoc failure in build
+ + 1749 Dump HttpDestination exchange queue
+ + 1750 PoolingHttpDestination creates ConnectionPool twice
+ + 1759 HTTP/2: producer can block in onReset
+ + 1766 JettyClientContainerProvider does not actually use common objects
+ correctly
+ + 1789 PropertyUserStoreTest failures in Windows
+ + 1790 HTTP/2: 100% CPU usage seen during close/shutdown of endpoint
+ + 1792 Accept ISO-8859-1 characters in response reason
+ + 1794 Config properties typos in session-store-cache.mod
+ + 1795 Fix session id manager workerName
+ + 1796 ReservedThreadExecutor defaulting to capacity=1 only
+ + 1797 JEP 238 - Multi-Release JAR files break bytecode scanning
+ + 1798 JMXify EatWhatYouKill
+ + 1804 Make EndPoint creation and destroy a non-blocking task
+ + 1805 ReservedThreadExecutor should start ReservedThreads lazily
+ + 1809 NPE: StandardDescriptorProcessor.visitSecurityConstraint() with null/no
+ security manager
+ + 1814 Move JavaVersion to jetty-util for future Java 9 support requirements
+ + 1816 HttpClientTest.testClientCannotValidateServerCertificate() hangs with
+ JDK 9
+ + 475546 ClosedChannelException when connection to HTTPS over HTTP proxy
+ with CONNECT
+
jetty-9.4.6.v20170531 - 31 May 2017
+ 523 TLS close behaviour breaking session resumption
+ 1108 Please improve logging in SslContextFactory when there are no approved
@@ -28,26 +124,59 @@ jetty-9.4.6.v20170531 - 31 May 2017
+ 1569 Allow setting of maxBinaryMessageSize to 0 in WebSocketPolicy
+ 1579 NPE in Quoted Quality CSV
-jetty-9.2.22.v20170606 - 06 June 2017
- + 920 no main manifest attribute, in jetty-runner-9.2.19.v20160908.jar
- + 1108 Please improve logging in SslContextFactory when there are no approved
- cipher suites
+jetty-9.3.21.v20170918 - 18 September 2017
+ + 487 JDK 9 build compatibility
+ + 1116 Support empty HTTP header values
+ 1357 RolloverFileOutputStream: No rollout performed at midnight
- + 1469 IllegalStateException in RolloverFileOutputStream
- + 1507 Negative delay Timer.schedule exception due to mismatched local and
- _logTimeZone values
- + 1532 RolloverFileOutputStream can't handle multiple instances
- + 1523 Update ALPN support for Java 8u131
- + 1556 A timing channel in Password.java
- + 1590 RolloverFileOutputStream not functioning in Jetty 9.2.21+
+ + 1469 RolloverFileOutputStream: IllegalStateException Task already scheduled
+ + 1507 RolloverFileOutputStream: Negative delay Timer.schedule exception
+ + 1513 RolloverFileOutputStream: can't handle multiple instances
+ + 1515 Improved RollOverFileOutputStream removeOldFiles() behavior
+ + 1556 Remove a timing channel in Password matching
+ + 1590 Improve RolloverFileOutputStream functionality with multiple TimeZones
+ + 1655 Improve extensibility of ServerConnector
+ + 1661 AbstractProxyServlet onProxyResponseFailure Error
+ + 1664 IPAccessHandler CIDR IP range check is incorrect
+ + 1685 Update ALPN support for Java 8u141
+ + 1687 HTTP2: Correcting missing callback notification when channel not found
+ + 1702 Update ALPN support for Java 8u144
+ + 1703 Improve HttpInput failure logging
+ + 1719 HTTP/2: Improve handling of queued requests
+ + 1741 Java 9 javadoc failure in build
+ + 1749 Dump HttpDestination exchange queue
+ + 1750 PoolingHttpDestination creates ConnectionPool twice
+ + 1759 HTTP/2: producer can block in onReset
+ + 1790 HTTP/2: 100% CPU usage seen during close/shutdown of endpoint
+ + 475546 ClosedChannelException when connection to HTTPS over HTTP proxy
+ with CONNECT
-jetty-9.3.20.v20170531 - 31 May 2017
+jetty-9.4.6.v20170531 - 31 May 2017
+ 523 TLS close behaviour breaking session resumption
+ 1108 Please improve logging in SslContextFactory when there are no approved
cipher suites
+ + 1505 Adding jetty.base.uri and jetty.home.uri
+ + 1514 websocket dump badly formatted
+ + 1516 Delay starting of WebSocketClient until an attempt to connect is made
+ + 1520 PropertyUserStore should extract packed config file
+ + 1526 MongoSessionDataStore old session scavenging is broken due to the
+ missing $ sign in "and" operation
+ 1527 Jetty BOM should not depend on jetty-parent
+ + 1528 Internal HttpClient usages should have common configurable technique
+ + 1536 Jetty BOM should include more artifacts
+ + 1538 NPE in Response.putHeaders
+ + 1539 JarFileResource mishandles paths with spaces
+ + 1544 Disabling JSR-356 doesn't indicate context it was disabled for
+ + 1546 Improve handling of quotes in cookies
+ + 1553 X509.isCertSign() can throw ArrayIndexOutOfBoundsException on
+ non-standard implementations
+ 1556 A timing channel in Password.java
+ + 1558 When creating WebAppContext without session-config and with NO_SESSIONS
+ throws NPE
+ 1567 XmlConfiguration will start the same object multiple times
+ + 1568 ServletUpgradeRequest mangles query strings containing percent-escapes
+ by re-escaping them
+ + 1569 Allow setting of maxBinaryMessageSize to 0 in WebSocketPolicy
+ + 1579 NPE in Quoted Quality CSV
jetty-9.4.5.v20170502 - 02 May 2017
+ 304 Review dead code - StringUtil.sidBytesToString
@@ -81,6 +210,27 @@ jetty-9.4.5.v20170502 - 02 May 2017
+ 1521 Prevent copy of jetty jars to lib/gcloud
+ 1523 Update ALPN support for Java 8u131
+jetty-9.3.20.v20170531 - 31 May 2017
+ + 523 TLS close behaviour breaking session resumption
+ + 1108 Please improve logging in SslContextFactory when there are no approved
+ cipher suites
+ + 1527 Jetty BOM should not depend on jetty-parent
+ + 1556 A timing channel in Password.java
+ + 1567 XmlConfiguration will start the same object multiple times
+
+jetty-9.2.22.v20170606 - 06 June 2017
+ + 920 no main manifest attribute, in jetty-runner-9.2.19.v20160908.jar
+ + 1108 Please improve logging in SslContextFactory when there are no approved
+ cipher suites
+ + 1357 RolloverFileOutputStream: No rollout performed at midnight
+ + 1469 IllegalStateException in RolloverFileOutputStream
+ + 1507 Negative delay Timer.schedule exception due to mismatched local and
+ _logTimeZone values
+ + 1532 RolloverFileOutputStream can't handle multiple instances
+ + 1523 Update ALPN support for Java 8u131
+ + 1556 A timing channel in Password.java
+ + 1590 RolloverFileOutputStream not functioning in Jetty 9.2.21+
+
jetty-9.3.19.v20170502 - 02 May 2017
+ 877 Programmatic servlet mappings cannot override mappings from
webdefault.xml using quickstart
diff --git a/apache-jstl/src/test/java/org/eclipse/jetty/jstl/JstlTest.java b/apache-jstl/src/test/java/org/eclipse/jetty/jstl/JstlTest.java
index 7c5d903a79d..1af557f1eae 100644
--- a/apache-jstl/src/test/java/org/eclipse/jetty/jstl/JstlTest.java
+++ b/apache-jstl/src/test/java/org/eclipse/jetty/jstl/JstlTest.java
@@ -18,11 +18,6 @@
package org.eclipse.jetty.jstl;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.junit.Assert.assertThat;
-
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -37,15 +32,22 @@ import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.toolchain.test.FS;
import org.eclipse.jetty.toolchain.test.JAR;
+import org.eclipse.jetty.toolchain.test.JDK;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.junit.AfterClass;
+import org.junit.Assume;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+
public class JstlTest
{
private static Server server;
@@ -54,6 +56,8 @@ public class JstlTest
@BeforeClass
public static void startServer() throws Exception
{
+ Assume.assumeFalse(JDK.IS_9);
+
// Setup Server
server = new Server();
ServerConnector connector = new ServerConnector(server);
@@ -98,7 +102,8 @@ public class JstlTest
@AfterClass
public static void stopServer() throws Exception
{
- server.stop();
+ if (server != null)
+ server.stop();
}
@Test
diff --git a/examples/embedded/pom.xml b/examples/embedded/pom.xml
index 1f820dc616e..d9b4a30a9a3 100644
--- a/examples/embedded/pom.xml
+++ b/examples/embedded/pom.xml
@@ -15,13 +15,6 @@
${project.groupId}.embedded
-
-
- com.google.guava
- guava
- 18.0
-
-
org.eclipse.jettyjetty-util-ajax
@@ -82,6 +75,16 @@
jetty-alpn-server${project.version}
+
+ org.eclipse.jetty
+ jetty-alpn-openjdk8-server
+ ${project.version}
+
+
+ org.eclipse.jetty
+ jetty-alpn-conscrypt-server
+ ${project.version}
+ org.eclipse.jettyjetty-annotations
@@ -138,4 +141,20 @@
test
+
+
+
+ jdk9
+
+ [1.9,)
+
+
+
+ org.eclipse.jetty
+ jetty-alpn-java-server
+ ${project.version}
+
+
+
+
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 d75498b0bac..5a4a068c4bc 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
@@ -19,8 +19,10 @@
package org.eclipse.jetty.embedded;
+import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
+import java.nio.file.Files;
import java.util.Date;
import java.util.EnumSet;
@@ -64,7 +66,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
public class Http2Server
{
public static void main(String... args) throws Exception
- {
+ {
Server server = new Server();
MBeanContainer mbContainer = new MBeanContainer(
@@ -72,7 +74,10 @@ public class Http2Server
server.addBean(mbContainer);
ServletContextHandler context = new ServletContextHandler(server, "/",ServletContextHandler.SESSIONS);
- context.setResourceBase("src/main/resources/docroot");
+ String docroot = "src/main/resources/docroot";
+ if (!new File(docroot).exists())
+ docroot = "examples/embedded/src/main/resources/docroot";
+ context.setResourceBase(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));
@@ -94,11 +99,14 @@ public class Http2Server
// SSL Context Factory for HTTPS and HTTP/2
String jetty_distro = System.getProperty("jetty.distro","../../jetty-distribution/target/distribution");
+ if (!new File(jetty_distro).exists())
+ jetty_distro = "jetty-distribution/target/distribution";
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.setKeyStorePath(jetty_distro + "/demo-base/etc/keystore");
sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
+ // sslContextFactory.setProvider("Conscrypt");
// HTTPS Configuration
HttpConfiguration https_config = new HttpConfiguration(http_config);
@@ -122,7 +130,6 @@ public class Http2Server
ALPN.debug=false;
server.start();
- //server.dumpStdErr();
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 931921402a2..127778a3310 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
@@ -20,12 +20,8 @@ package org.eclipse.jetty.embedded;
import java.io.File;
import java.io.FileNotFoundException;
-import java.lang.reflect.Method;
import java.security.Provider;
import java.security.Security;
-import java.util.function.BiFunction;
-
-import javax.net.ssl.SSLEngine;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.server.Connector;
@@ -43,7 +39,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
public class ManyConnectors
{
public static void main( String[] args ) 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
@@ -97,7 +93,6 @@ public class ManyConnectors
sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath());
sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
- sslContextFactory.setProvider("Conscrypt");
// HTTPS Configuration
// A new HttpConfiguration object is needed for the next connector and
@@ -137,18 +132,6 @@ public class ManyConnectors
// Start the server
server.start();
- SSLEngine engine = sslContextFactory.newSSLEngine();
- System.err.println(engine);
- System.err.println(engine.getClass());
- for (Method m : engine.getClass().getMethods())
- {
- System.err.println(m);
- }
-
-
-
-
-
server.join();
}
}
diff --git a/examples/embedded/src/main/resources/jetty-logging.properties b/examples/embedded/src/main/resources/jetty-logging.properties
index 7bffba83dbb..810f9896c7e 100644
--- a/examples/embedded/src/main/resources/jetty-logging.properties
+++ b/examples/embedded/src/main/resources/jetty-logging.properties
@@ -8,3 +8,4 @@
#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
diff --git a/jetty-alpn/jetty-alpn-client/pom.xml b/jetty-alpn/jetty-alpn-client/pom.xml
index 7c406851c21..4766cc66123 100644
--- a/jetty-alpn/jetty-alpn-client/pom.xml
+++ b/jetty-alpn/jetty-alpn-client/pom.xml
@@ -50,12 +50,6 @@
jetty-io${project.version}
-
- org.eclipse.jetty.alpn
- alpn-api
- ${alpn.api.version}
- provided
- org.eclipse.jetty.toolchainjetty-test-helper
diff --git a/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java
index cc31f3b9d50..b224c86e05d 100644
--- a/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java
+++ b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java
@@ -24,14 +24,13 @@ import java.util.concurrent.Executor;
import javax.net.ssl.SSLEngine;
-import org.eclipse.jetty.alpn.ALPN;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.NegotiatingClientConnection;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
-public class ALPNClientConnection extends NegotiatingClientConnection implements ALPN.ClientProvider
+public class ALPNClientConnection extends NegotiatingClientConnection
{
private static final Logger LOG = Log.getLogger(ALPNClientConnection.class);
@@ -41,41 +40,18 @@ public class ALPNClientConnection extends NegotiatingClientConnection implements
{
super(endPoint, executor, sslEngine, connectionFactory, context);
this.protocols = protocols;
- ALPN.put(sslEngine, this);
}
- @Override
- public void unsupported()
- {
- ALPN.remove(getSSLEngine());
- completed();
- }
-
- @Override
- public List protocols()
+ public List getProtocols()
{
return protocols;
}
- @Override
public void selected(String protocol)
{
- if (protocols.contains(protocol))
- {
- ALPN.remove(getSSLEngine());
- completed();
- }
- else
- {
- LOG.info("Could not negotiate protocol: server [{}] - client {}", protocol, protocols);
+ if (protocol==null || !protocols.contains(protocol))
close();
- }
- }
-
- @Override
- public void close()
- {
- ALPN.remove(getSSLEngine());
- super.close();
+ else
+ super.completed();
}
}
diff --git a/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java
index 26657b5c490..1fc8b560956 100644
--- a/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java
+++ b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java
@@ -19,6 +19,7 @@
package org.eclipse.jetty.alpn.client;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -31,17 +32,19 @@ import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.NegotiatingClientConnectionFactory;
-import org.eclipse.jetty.io.ssl.ALPNProcessor;
+import org.eclipse.jetty.io.ssl.ALPNProcessor.Client;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.io.ssl.SslHandshakeListener;
-import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFactory implements SslHandshakeListener
{
- private final SslHandshakeListener alpnListener = new ALPNListener();
+ private static final Logger LOG = Log.getLogger(ALPNClientConnectionFactory.class);
+
+ private final List processors = new ArrayList<>();
private final Executor executor;
private final List protocols;
- private final ALPNProcessor.Client alpnProcessor;
public ALPNClientConnectionFactory(Executor executor, ClientConnectionFactory connectionFactory, List protocols)
{
@@ -50,39 +53,64 @@ public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFact
throw new IllegalArgumentException("ALPN protocol list cannot be empty");
this.executor = executor;
this.protocols = protocols;
- Iterator processors = ServiceLoader.load(ALPNProcessor.Client.class).iterator();
- alpnProcessor = processors.hasNext() ? processors.next() : ALPNProcessor.Client.NOOP;
- }
- public ALPNProcessor.Client getALPNProcessor()
- {
- return alpnProcessor;
+ IllegalStateException failure = new IllegalStateException("No Client ALPNProcessors!");
+
+ // Use a for loop on iterator so load exceptions can be caught and ignored
+ for (Iterator i = ServiceLoader.load(Client.class).iterator(); i.hasNext();)
+ {
+ Client processor;
+ try
+ {
+ processor = i.next();
+ }
+ catch(Throwable x)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug(x);
+ failure.addSuppressed(x);
+ continue;
+ }
+
+ try
+ {
+ processor.init();
+ processors.add(processor);
+ }
+ catch (Throwable x)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Could not initialize " + processor, x);
+ failure.addSuppressed(x);
+ }
+ }
+
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("protocols: {}", protocols);
+ LOG.debug("processors: {}", processors);
+ }
+
+ if (processors.isEmpty())
+ throw failure;
}
@Override
public Connection newConnection(EndPoint endPoint, Map context) throws IOException
{
- SSLEngine sslEngine = (SSLEngine)context.get(SslClientConnectionFactory.SSL_ENGINE_CONTEXT_KEY);
- getALPNProcessor().configure(sslEngine, protocols);
- ContainerLifeCycle connector = (ContainerLifeCycle)context.get(ClientConnectionFactory.CONNECTOR_CONTEXT_KEY);
- // Method addBean() has set semantic, so the listener is added only once.
- connector.addBean(alpnListener);
- ALPNClientConnection connection = new ALPNClientConnection(endPoint, executor, getClientConnectionFactory(),
- sslEngine, context, protocols);
- return customize(connection, context);
- }
-
- private class ALPNListener implements SslHandshakeListener
- {
- @Override
- public void handshakeSucceeded(Event event)
- {
- getALPNProcessor().process(event.getSSLEngine());
- }
-
- @Override
- public void handshakeFailed(Event event, Throwable failure)
+ SSLEngine engine = (SSLEngine)context.get(SslClientConnectionFactory.SSL_ENGINE_CONTEXT_KEY);
+ for (Client processor : processors)
{
+ if (processor.appliesTo(engine))
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} for {} on {}", processor, engine, endPoint);
+ ALPNClientConnection connection = new ALPNClientConnection(endPoint, executor, getClientConnectionFactory(),
+ engine, context, protocols);
+ processor.configure(engine, connection);
+ return customize(connection, context);
+ }
}
+ throw new IllegalStateException("No ALPNProcessor for " + engine);
}
}
diff --git a/jetty-alpn/jetty-alpn-conscrypt-client/pom.xml b/jetty-alpn/jetty-alpn-conscrypt-client/pom.xml
new file mode 100644
index 00000000000..a6866b10edc
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-conscrypt-client/pom.xml
@@ -0,0 +1,39 @@
+
+
+
+
+ org.eclipse.jetty
+ jetty-alpn-parent
+ 10.0.0-SNAPSHOT
+
+
+ 4.0.0
+ jetty-alpn-conscrypt-client
+ Jetty :: ALPN :: Conscrypt Client Implementation
+
+
+ ${project.groupId}.alpn.java.client
+ 1.0.0.RC10
+
+
+
+
+ org.conscrypt
+ conscrypt-openjdk-uber
+ ${conscrypt.version}
+
+
+ org.eclipse.jetty
+ jetty-alpn-client
+ ${project.version}
+
+
+ org.eclipse.jetty.http2
+ http2-client
+ ${project.version}
+ test
+
+
+
diff --git a/jetty-alpn/jetty-alpn-conscrypt-client/src/main/java/org/eclipse/jetty/alpn/conscrypt/client/ConscryptClientALPNProcessor.java b/jetty-alpn/jetty-alpn-conscrypt-client/src/main/java/org/eclipse/jetty/alpn/conscrypt/client/ConscryptClientALPNProcessor.java
new file mode 100644
index 00000000000..20f8f146ff7
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-conscrypt-client/src/main/java/org/eclipse/jetty/alpn/conscrypt/client/ConscryptClientALPNProcessor.java
@@ -0,0 +1,107 @@
+//
+// ========================================================================
+// 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.
+// ========================================================================
+//
+
+package org.eclipse.jetty.alpn.conscrypt.client;
+
+import java.lang.reflect.Method;
+import java.nio.charset.StandardCharsets;
+import java.security.Security;
+
+import javax.net.ssl.SSLEngine;
+
+import org.conscrypt.OpenSSLProvider;
+import org.eclipse.jetty.alpn.client.ALPNClientConnection;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.ssl.ALPNProcessor;
+import org.eclipse.jetty.io.ssl.SslConnection;
+import org.eclipse.jetty.io.ssl.SslHandshakeListener;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class ConscryptClientALPNProcessor implements ALPNProcessor.Client
+{
+ private static final Logger LOG = Log.getLogger(ConscryptClientALPNProcessor.class);
+
+ @Override
+ public void init()
+ {
+ if (Security.getProvider("Conscrypt")==null)
+ {
+ Security.addProvider(new OpenSSLProvider());
+ if (LOG.isDebugEnabled())
+ LOG.debug("Added Conscrypt provider");
+ }
+ }
+
+ @Override
+ public boolean appliesTo(SSLEngine sslEngine)
+ {
+ return sslEngine.getClass().getName().startsWith("org.conscrypt.");
+ }
+
+ @Override
+ public void configure(SSLEngine sslEngine, Connection connection)
+ {
+ try
+ {
+ Method setAlpnProtocols = sslEngine.getClass().getDeclaredMethod("setAlpnProtocols", String[].class);
+ setAlpnProtocols.setAccessible(true);
+ ALPNClientConnection alpn = (ALPNClientConnection)connection;
+ String[] protocols = alpn.getProtocols().toArray(new String[0]);
+ setAlpnProtocols.invoke(sslEngine, (Object)protocols);
+ ((SslConnection.DecryptedEndPoint)connection.getEndPoint()).getSslConnection()
+ .addHandshakeListener(new ALPNListener(alpn));
+ }
+ catch (RuntimeException x)
+ {
+ throw x;
+ }
+ catch (Exception x)
+ {
+ throw new RuntimeException(x);
+ }
+ }
+
+ private final class ALPNListener implements SslHandshakeListener
+ {
+ private final ALPNClientConnection alpnConnection;
+
+ private ALPNListener(ALPNClientConnection connection)
+ {
+ alpnConnection = connection;
+ }
+
+ @Override
+ public void handshakeSucceeded(Event event)
+ {
+ try
+ {
+ SSLEngine sslEngine = alpnConnection.getSSLEngine();
+ Method method = sslEngine.getClass().getDeclaredMethod("getAlpnSelectedProtocol");
+ method.setAccessible(true);
+ String protocol = new String((byte[])method.invoke(sslEngine), StandardCharsets.US_ASCII);
+ alpnConnection.selected(protocol);
+ }
+ catch (Throwable e)
+ {
+ alpnConnection.selected(null);
+ LOG.warn(e);
+ }
+ }
+ }
+}
diff --git a/jetty-alpn/jetty-alpn-conscrypt-client/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Client b/jetty-alpn/jetty-alpn-conscrypt-client/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Client
new file mode 100644
index 00000000000..bfb6cd8b55f
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-conscrypt-client/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Client
@@ -0,0 +1 @@
+org.eclipse.jetty.alpn.conscrypt.client.ConscryptClientALPNProcessor
diff --git a/jetty-alpn/jetty-alpn-conscrypt-client/src/test/java/org/eclipse/jetty/alpn/java/client/ConscryptHTTP2Client.java b/jetty-alpn/jetty-alpn-conscrypt-client/src/test/java/org/eclipse/jetty/alpn/java/client/ConscryptHTTP2Client.java
new file mode 100644
index 00000000000..576d544f482
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-conscrypt-client/src/test/java/org/eclipse/jetty/alpn/java/client/ConscryptHTTP2Client.java
@@ -0,0 +1,89 @@
+//
+// ========================================================================
+// 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.
+// ========================================================================
+//
+
+package org.eclipse.jetty.alpn.java.client;
+
+import java.net.InetSocketAddress;
+import java.security.Security;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.conscrypt.OpenSSLProvider;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.client.HTTP2Client;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.FuturePromise;
+import org.eclipse.jetty.util.Jetty;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+public class ConscryptHTTP2Client
+{
+ public static void main(String[] args) throws Exception
+ {
+ Security.addProvider(new OpenSSLProvider());
+ SslContextFactory sslContextFactory = new SslContextFactory();
+ sslContextFactory.setProvider("Conscrypt");
+ HTTP2Client client = new HTTP2Client();
+ client.addBean(sslContextFactory);
+ client.start();
+
+ String host = "webtide.com";
+ int port = 443;
+
+ FuturePromise sessionPromise = new FuturePromise<>();
+ client.connect(sslContextFactory, new InetSocketAddress(host, port), new Session.Listener.Adapter(), sessionPromise);
+ Session session = sessionPromise.get(5, TimeUnit.SECONDS);
+
+ HttpFields requestFields = new HttpFields();
+ requestFields.put("User-Agent", client.getClass().getName() + "/" + Jetty.VERSION);
+ MetaData.Request metaData = new MetaData.Request("GET", new HttpURI("https://" + host + ":" + port + "/"), HttpVersion.HTTP_2, requestFields);
+ HeadersFrame headersFrame = new HeadersFrame(metaData, null, true);
+ CountDownLatch latch = new CountDownLatch(1);
+ session.newStream(headersFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+ {
+ @Override
+ public void onHeaders(Stream stream, HeadersFrame frame)
+ {
+ System.err.println(frame);
+ if (frame.isEndStream())
+ latch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataFrame frame, Callback callback)
+ {
+ System.err.println(frame);
+ callback.succeeded();
+ if (frame.isEndStream())
+ latch.countDown();
+ }
+ });
+
+ latch.await(5, TimeUnit.SECONDS);
+
+ client.stop();
+ }
+}
diff --git a/jetty-alpn/jetty-alpn-conscrypt-client/src/test/resources/jetty-logging.properties b/jetty-alpn/jetty-alpn-conscrypt-client/src/test/resources/jetty-logging.properties
new file mode 100644
index 00000000000..d96a696f82e
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-conscrypt-client/src/test/resources/jetty-logging.properties
@@ -0,0 +1,2 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+#org.eclipse.jetty.LEVEL=DEBUG
diff --git a/jetty-alpn/jetty-alpn-conscrypt-server/pom.xml b/jetty-alpn/jetty-alpn-conscrypt-server/pom.xml
new file mode 100644
index 00000000000..7fa067bbbb5
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-conscrypt-server/pom.xml
@@ -0,0 +1,43 @@
+
+
+
+ org.eclipse.jetty
+ jetty-alpn-parent
+ 10.0.0-SNAPSHOT
+
+
+ 4.0.0
+ jetty-alpn-conscrypt-server
+ Jetty :: ALPN :: Conscrypt Server Implementation
+
+
+ ${project.groupId}.alpn.conscrypt.server
+ 1.0.0.RC10
+
+
+
+
+ org.conscrypt
+ conscrypt-openjdk-uber
+ ${conscrypt.version}
+
+
+ org.eclipse.jetty
+ jetty-alpn-server
+ ${project.version}
+
+
+ org.eclipse.jetty
+ jetty-io
+ ${project.version}
+
+
+
+ org.eclipse.jetty.http2
+ http2-server
+ ${project.version}
+ test
+
+
+
diff --git a/jetty-alpn/jetty-alpn-conscrypt-server/src/main/java/org/eclipse/jetty/alpn/conscrypt/server/ConscryptServerALPNProcessor.java b/jetty-alpn/jetty-alpn-conscrypt-server/src/main/java/org/eclipse/jetty/alpn/conscrypt/server/ConscryptServerALPNProcessor.java
new file mode 100644
index 00000000000..0d8acbbf874
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-conscrypt-server/src/main/java/org/eclipse/jetty/alpn/conscrypt/server/ConscryptServerALPNProcessor.java
@@ -0,0 +1,115 @@
+//
+// ========================================================================
+// 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.
+// ========================================================================
+//
+
+package org.eclipse.jetty.alpn.conscrypt.server;
+
+import java.lang.reflect.Method;
+import java.security.Security;
+import java.util.List;
+import java.util.function.BiFunction;
+
+import javax.net.ssl.SSLEngine;
+
+import org.conscrypt.OpenSSLProvider;
+import org.eclipse.jetty.alpn.server.ALPNServerConnection;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.ssl.ALPNProcessor;
+import org.eclipse.jetty.io.ssl.SslConnection.DecryptedEndPoint;
+import org.eclipse.jetty.io.ssl.SslHandshakeListener;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class ConscryptServerALPNProcessor implements ALPNProcessor.Server
+{
+ private static final Logger LOG = Log.getLogger(ConscryptServerALPNProcessor.class);
+
+ @Override
+ public void init()
+ {
+ if (Security.getProvider("Conscrypt")==null)
+ {
+ Security.addProvider(new OpenSSLProvider());
+ if (LOG.isDebugEnabled())
+ LOG.debug("Added Conscrypt provider");
+ }
+ }
+
+ @Override
+ public boolean appliesTo(SSLEngine sslEngine)
+ {
+ return sslEngine.getClass().getName().startsWith("org.conscrypt.");
+ }
+
+ @Override
+ public void configure(SSLEngine sslEngine,Connection connection)
+ {
+ try
+ {
+ Method method = sslEngine.getClass().getMethod("setHandshakeApplicationProtocolSelector", BiFunction.class);
+ method.setAccessible(true);
+ method.invoke(sslEngine,new ALPNCallback((ALPNServerConnection)connection));
+ }
+ catch (RuntimeException x)
+ {
+ throw x;
+ }
+ catch (Exception x)
+ {
+ throw new RuntimeException(x);
+ }
+ }
+
+ private final class ALPNCallback implements BiFunction,String>, SslHandshakeListener
+ {
+ private final ALPNServerConnection alpnConnection;
+
+ private ALPNCallback(ALPNServerConnection connection)
+ {
+ alpnConnection = connection;
+ ((DecryptedEndPoint)alpnConnection.getEndPoint()).getSslConnection().addHandshakeListener(this);
+ }
+
+ @Override
+ public String apply(SSLEngine engine, List protocols)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("apply {} {}", alpnConnection, protocols);
+ alpnConnection.select(protocols);
+ return alpnConnection.getProtocol();
+ }
+
+ @Override
+ public void handshakeSucceeded(Event event)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("handshakeSucceeded {} {}", alpnConnection, event);
+ if (alpnConnection.getProtocol()==null)
+ {
+ LOG.warn("No ALPN callback! {} {}",alpnConnection, event);
+ alpnConnection.unsupported();
+ }
+ }
+
+ @Override
+ public void handshakeFailed(Event event, Throwable failure)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("handshakeFailed {} {} {}", alpnConnection, event, failure);
+ }
+ }
+}
diff --git a/jetty-alpn/jetty-alpn-conscrypt-server/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Server b/jetty-alpn/jetty-alpn-conscrypt-server/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Server
new file mode 100644
index 00000000000..255e5b479af
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-conscrypt-server/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Server
@@ -0,0 +1 @@
+org.eclipse.jetty.alpn.conscrypt.server.ConscryptServerALPNProcessor
diff --git a/jetty-alpn/jetty-alpn-conscrypt-server/src/test/java/org/eclipse/jetty/alpn/conscrypt/server/ConscryptHTTP2Server.java b/jetty-alpn/jetty-alpn-conscrypt-server/src/test/java/org/eclipse/jetty/alpn/conscrypt/server/ConscryptHTTP2Server.java
new file mode 100644
index 00000000000..f663cd41c2c
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-conscrypt-server/src/test/java/org/eclipse/jetty/alpn/conscrypt/server/ConscryptHTTP2Server.java
@@ -0,0 +1,72 @@
+//
+// ========================================================================
+// 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.
+// ========================================================================
+//
+
+package org.eclipse.jetty.alpn.conscrypt.server;
+
+import java.security.Security;
+
+import org.conscrypt.OpenSSLProvider;
+import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
+import org.eclipse.jetty.http2.HTTP2Cipher;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+/**
+ * Test server that verifies that the Conscrypt ALPN mechanism works.
+ */
+public class ConscryptHTTP2Server
+{
+ public static void main(String[] args) throws Exception
+ {
+ Security.addProvider(new OpenSSLProvider());
+
+ Server server = new Server();
+
+ HttpConfiguration httpsConfig = new HttpConfiguration();
+ httpsConfig.setSecureScheme("https");
+ httpsConfig.setSecurePort(8443);
+ httpsConfig.setSendXPoweredBy(true);
+ httpsConfig.setSendServerVersion(true);
+ httpsConfig.addCustomizer(new SecureRequestCustomizer());
+
+ SslContextFactory sslContextFactory = new SslContextFactory();
+ sslContextFactory.setProvider("Conscrypt");
+ sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
+ sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
+ sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
+ sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
+
+ HttpConnectionFactory http = new HttpConnectionFactory(httpsConfig);
+ HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpsConfig);
+ ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
+ alpn.setDefaultProtocol(http.getProtocol());
+ SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, alpn.getProtocol());
+
+ ServerConnector http2Connector = new ServerConnector(server, ssl, alpn, h2, http);
+ http2Connector.setPort(8443);
+ server.addConnector(http2Connector);
+
+ server.start();
+ }
+}
diff --git a/jetty-alpn/jetty-alpn-conscrypt-server/src/test/resources/jetty-logging.properties b/jetty-alpn/jetty-alpn-conscrypt-server/src/test/resources/jetty-logging.properties
new file mode 100644
index 00000000000..c391f84e35b
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-conscrypt-server/src/test/resources/jetty-logging.properties
@@ -0,0 +1,3 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+#org.eclipse.jetty.LEVEL=DEBUG
+#org.eclipse.jetty.alpn.LEVEL=DEBUG
diff --git a/jetty-alpn/jetty-alpn-conscrypt-server/src/test/resources/keystore.jks b/jetty-alpn/jetty-alpn-conscrypt-server/src/test/resources/keystore.jks
new file mode 100644
index 00000000000..d6592f95ee9
Binary files /dev/null and b/jetty-alpn/jetty-alpn-conscrypt-server/src/test/resources/keystore.jks differ
diff --git a/jetty-alpn/jetty-alpn-java-client/pom.xml b/jetty-alpn/jetty-alpn-java-client/pom.xml
index 3ca778c136d..f11438dcc8a 100644
--- a/jetty-alpn/jetty-alpn-java-client/pom.xml
+++ b/jetty-alpn/jetty-alpn-java-client/pom.xml
@@ -33,22 +33,15 @@
org.eclipse.jetty
- jetty-io
+ jetty-alpn-client${project.version}
-
- org.eclipse.jetty.alpn
- alpn-api
- ${alpn.api.version}
-
-
org.eclipse.jetty.http2http2-client${project.version}test
-
diff --git a/jetty-alpn/jetty-alpn-java-client/src/main/java/org/eclipse/jetty/alpn/java/client/JDK9ClientALPNProcessor.java b/jetty-alpn/jetty-alpn-java-client/src/main/java/org/eclipse/jetty/alpn/java/client/JDK9ClientALPNProcessor.java
index c9f7bb4059c..432adcde610 100644
--- a/jetty-alpn/jetty-alpn-java-client/src/main/java/org/eclipse/jetty/alpn/java/client/JDK9ClientALPNProcessor.java
+++ b/jetty-alpn/jetty-alpn-java-client/src/main/java/org/eclipse/jetty/alpn/java/client/JDK9ClientALPNProcessor.java
@@ -18,38 +18,66 @@
package org.eclipse.jetty.alpn.java.client;
-import java.io.UncheckedIOException;
import java.util.List;
import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLException;
import javax.net.ssl.SSLParameters;
-import org.eclipse.jetty.alpn.ALPN;
+import org.eclipse.jetty.alpn.client.ALPNClientConnection;
+import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.ssl.ALPNProcessor;
+import org.eclipse.jetty.io.ssl.SslConnection.DecryptedEndPoint;
+import org.eclipse.jetty.io.ssl.SslHandshakeListener;
+import org.eclipse.jetty.util.JavaVersion;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
public class JDK9ClientALPNProcessor implements ALPNProcessor.Client
{
+ private static final Logger LOG = Log.getLogger(JDK9ClientALPNProcessor.class);
+
@Override
- public void configure(SSLEngine sslEngine, List protocols)
+ public void init()
{
- SSLParameters sslParameters = sslEngine.getSSLParameters();
- sslParameters.setApplicationProtocols(protocols.toArray(new String[0]));
- sslEngine.setSSLParameters(sslParameters);
+ if (JavaVersion.VERSION.getPlatform()<9)
+ throw new IllegalStateException(this + " not applicable for java "+JavaVersion.VERSION);
}
@Override
- public void process(SSLEngine sslEngine)
+ public boolean appliesTo(SSLEngine sslEngine)
{
- try
+ Module module = sslEngine.getClass().getModule();
+ return module!=null && "java.base".equals(module.getName());
+ }
+
+ @Override
+ public void configure(SSLEngine sslEngine, Connection connection)
+ {
+ ALPNClientConnection alpn = (ALPNClientConnection)connection;
+ SSLParameters sslParameters = sslEngine.getSSLParameters();
+ List protocols = alpn.getProtocols();
+ sslParameters.setApplicationProtocols(protocols.toArray(new String[protocols.size()]));
+ sslEngine.setSSLParameters(sslParameters);
+ ((DecryptedEndPoint)connection.getEndPoint()).getSslConnection()
+ .addHandshakeListener(new ALPNListener(alpn));
+ }
+
+ private final class ALPNListener implements SslHandshakeListener
+ {
+ private final ALPNClientConnection alpnConnection;
+
+ private ALPNListener(ALPNClientConnection connection)
{
- ALPN.ClientProvider provider = (ALPN.ClientProvider)ALPN.get(sslEngine);
- if (provider != null)
- provider.selected(sslEngine.getApplicationProtocol());
+ alpnConnection = connection;
}
- catch (SSLException x)
+
+ @Override
+ public void handshakeSucceeded(Event event)
{
- throw new UncheckedIOException(x);
+ String protocol = alpnConnection.getSSLEngine().getApplicationProtocol();
+ if (LOG.isDebugEnabled())
+ LOG.debug("selected protocol {}", protocol);
+ alpnConnection.selected(protocol);
}
}
}
diff --git a/jetty-alpn/jetty-alpn-java-client/src/test/java/org/eclipse/jetty/alpn/java/client/JDK9HTTP2Client.java b/jetty-alpn/jetty-alpn-java-client/src/test/java/org/eclipse/jetty/alpn/java/client/JDK9HTTP2Client.java
index 3ff2219dd28..d33278f17d5 100644
--- a/jetty-alpn/jetty-alpn-java-client/src/test/java/org/eclipse/jetty/alpn/java/client/JDK9HTTP2Client.java
+++ b/jetty-alpn/jetty-alpn-java-client/src/test/java/org/eclipse/jetty/alpn/java/client/JDK9HTTP2Client.java
@@ -42,16 +42,16 @@ public class JDK9HTTP2Client
public static void main(String[] args) throws Exception
{
HTTP2Client client = new HTTP2Client();
- SslContextFactory sslContextFactory = new SslContextFactory(true);
+ SslContextFactory sslContextFactory = new SslContextFactory();
client.addBean(sslContextFactory);
client.start();
- String host = "localhost";
- int port = 8443;
+ String host = "webtide.com";
+ int port = 443;
FuturePromise sessionPromise = new FuturePromise<>();
client.connect(sslContextFactory, new InetSocketAddress(host, port), new Session.Listener.Adapter(), sessionPromise);
- Session session = sessionPromise.get(555, TimeUnit.SECONDS);
+ Session session = sessionPromise.get(5, TimeUnit.SECONDS);
HttpFields requestFields = new HttpFields();
requestFields.put("User-Agent", client.getClass().getName() + "/" + Jetty.VERSION);
diff --git a/jetty-alpn/jetty-alpn-java-server/pom.xml b/jetty-alpn/jetty-alpn-java-server/pom.xml
index 9c08813aa97..c36114cd8bf 100644
--- a/jetty-alpn/jetty-alpn-java-server/pom.xml
+++ b/jetty-alpn/jetty-alpn-java-server/pom.xml
@@ -40,12 +40,10 @@
alpn-api${alpn.api.version}
-
org.eclipse.jetty
- jetty-server
+ jetty-alpn-server${project.version}
- testorg.eclipse.jetty.http2
@@ -53,12 +51,6 @@
${project.version}test
-
- org.eclipse.jetty
- jetty-alpn-server
- ${project.version}
- test
- junitjunit
diff --git a/jetty-alpn/jetty-alpn-java-server/src/main/java/org/eclipse/jetty/alpn/java/server/JDK9ServerALPNProcessor.java b/jetty-alpn/jetty-alpn-java-server/src/main/java/org/eclipse/jetty/alpn/java/server/JDK9ServerALPNProcessor.java
index 606ac5ef796..f9e0313a211 100644
--- a/jetty-alpn/jetty-alpn-java-server/src/main/java/org/eclipse/jetty/alpn/java/server/JDK9ServerALPNProcessor.java
+++ b/jetty-alpn/jetty-alpn-java-server/src/main/java/org/eclipse/jetty/alpn/java/server/JDK9ServerALPNProcessor.java
@@ -19,13 +19,16 @@
package org.eclipse.jetty.alpn.java.server;
import java.util.List;
+import java.util.function.BiFunction;
import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLException;
-import org.eclipse.jetty.alpn.ALPN;
+import org.eclipse.jetty.alpn.server.ALPNServerConnection;
+import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.ssl.ALPNProcessor;
+import org.eclipse.jetty.io.ssl.SslConnection;
import org.eclipse.jetty.io.ssl.SslHandshakeListener;
+import org.eclipse.jetty.util.JavaVersion;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@@ -34,40 +37,61 @@ public class JDK9ServerALPNProcessor implements ALPNProcessor.Server, SslHandsha
private static final Logger LOG = Log.getLogger(JDK9ServerALPNProcessor.class);
@Override
- public void configure(SSLEngine sslEngine)
+ public void init()
{
- sslEngine.setHandshakeApplicationProtocolSelector(this::process);
- }
-
- private String process(SSLEngine sslEngine, List protocols)
- {
- try
- {
- if (LOG.isDebugEnabled())
- LOG.debug("ALPN selecting among client{}", protocols);
- ALPN.ServerProvider provider = (ALPN.ServerProvider)ALPN.remove(sslEngine);
- return provider == null ? "" : provider.select(protocols);
- }
- catch (SSLException x)
- {
- return null;
- }
+ if (JavaVersion.VERSION.getPlatform()<9)
+ throw new IllegalStateException(this + " not applicable for java "+JavaVersion.VERSION);
}
@Override
- public void handshakeSucceeded(Event event)
+ public boolean appliesTo(SSLEngine sslEngine)
{
- ALPN.ServerProvider provider = (ALPN.ServerProvider)ALPN.remove(event.getSSLEngine());
- if (provider != null)
- {
- if (LOG.isDebugEnabled())
- LOG.debug("ALPN unsupported by client");
- provider.unsupported();
- }
+ Module module = sslEngine.getClass().getModule();
+ return module!=null && "java.base".equals(module.getName());
}
@Override
- public void handshakeFailed(Event event, Throwable failure)
+ public void configure(SSLEngine sslEngine, Connection connection)
{
+ sslEngine.setHandshakeApplicationProtocolSelector(new ALPNCallback((ALPNServerConnection)connection));
+ }
+
+ private final class ALPNCallback implements BiFunction,String>, SslHandshakeListener
+ {
+ private final ALPNServerConnection alpnConnection;
+
+ private ALPNCallback(ALPNServerConnection connection)
+ {
+ alpnConnection = connection;
+ ((SslConnection.DecryptedEndPoint)alpnConnection.getEndPoint()).getSslConnection().addHandshakeListener(this);
+ }
+
+ @Override
+ public String apply(SSLEngine engine, List protocols)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("apply {} {}", alpnConnection, protocols);
+ alpnConnection.select(protocols);
+ return alpnConnection.getProtocol();
+ }
+
+ @Override
+ public void handshakeSucceeded(Event event)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("handshakeSucceeded {} {}", alpnConnection, event);
+ if (alpnConnection.getProtocol()==null)
+ {
+ LOG.warn("No ALPN callback! {} {}",alpnConnection, event);
+ alpnConnection.unsupported();
+ }
+ }
+
+ @Override
+ public void handshakeFailed(Event event, Throwable failure)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("handshakeFailed {} {} {}", alpnConnection, event, failure);
+ }
}
}
diff --git a/jetty-alpn/jetty-alpn-openjdk8-client/pom.xml b/jetty-alpn/jetty-alpn-openjdk8-client/pom.xml
new file mode 100644
index 00000000000..250ff6a7333
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-openjdk8-client/pom.xml
@@ -0,0 +1,38 @@
+
+
+
+
+ org.eclipse.jetty
+ jetty-alpn-parent
+ 10.0.0-SNAPSHOT
+
+
+ 4.0.0
+ jetty-alpn-openjdk8-client
+ Jetty :: ALPN :: OpenJDK8 Client Implementation
+
+
+ ${project.groupId}.alpn.java.client
+
+
+
+
+ org.eclipse.jetty
+ jetty-alpn-client
+ ${project.version}
+
+
+ org.eclipse.jetty.alpn
+ alpn-api
+ ${alpn.api.version}
+
+
+ org.eclipse.jetty.http2
+ http2-client
+ ${project.version}
+ test
+
+
+
diff --git a/jetty-alpn/jetty-alpn-openjdk8-client/src/main/java/org/eclipse/jetty/alpn/java/client/OpenJDK8ClientALPNProcessor.java b/jetty-alpn/jetty-alpn-openjdk8-client/src/main/java/org/eclipse/jetty/alpn/java/client/OpenJDK8ClientALPNProcessor.java
new file mode 100644
index 00000000000..539cb6cd872
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-openjdk8-client/src/main/java/org/eclipse/jetty/alpn/java/client/OpenJDK8ClientALPNProcessor.java
@@ -0,0 +1,104 @@
+//
+// ========================================================================
+// 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.
+// ========================================================================
+//
+
+package org.eclipse.jetty.alpn.java.client;
+
+import java.util.List;
+
+import javax.net.ssl.SSLEngine;
+
+import org.eclipse.jetty.alpn.ALPN;
+import org.eclipse.jetty.alpn.client.ALPNClientConnection;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.ssl.ALPNProcessor;
+import org.eclipse.jetty.util.JavaVersion;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class OpenJDK8ClientALPNProcessor implements ALPNProcessor.Client
+{
+ private static final Logger LOG = Log.getLogger(OpenJDK8ClientALPNProcessor.class);
+
+ @Override
+ public void init()
+ {
+ if (JavaVersion.VERSION.getPlatform()!=8)
+ throw new IllegalStateException(this + " not applicable for java "+JavaVersion.VERSION);
+ if (ALPN.class.getClassLoader()!=null)
+ throw new IllegalStateException(this + " must be on JVM boot classpath");
+ }
+
+ @Override
+ public boolean appliesTo(SSLEngine sslEngine)
+ {
+ return sslEngine.getClass().getName().startsWith("sun.security.ssl.");
+ }
+
+ @Override
+ public void configure(SSLEngine sslEngine, Connection connection)
+ {
+ connection.addListener(new ALPNListener((ALPNClientConnection)connection));
+ }
+
+ private final class ALPNListener implements ALPN.ClientProvider, Connection.Listener
+ {
+ private final ALPNClientConnection alpnConnection;
+
+ private ALPNListener(ALPNClientConnection connection)
+ {
+ alpnConnection = connection;
+ }
+
+ @Override
+ public void onOpened(Connection connection)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("onOpened {}", alpnConnection);
+ ALPN.put(alpnConnection.getSSLEngine(), this);
+ }
+
+ @Override
+ public void onClosed(Connection connection)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("onClosed {}", alpnConnection);
+ ALPN.remove(alpnConnection.getSSLEngine());
+ }
+
+ @Override
+ public List protocols()
+ {
+ return alpnConnection.getProtocols();
+ }
+
+ @Override
+ public void unsupported()
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("unsupported {}", alpnConnection);
+ ALPN.remove(alpnConnection.getSSLEngine());
+ alpnConnection.selected(null);
+ }
+
+ @Override
+ public void selected(String protocol)
+ {
+ alpnConnection.selected(protocol);
+ }
+ }
+}
diff --git a/jetty-alpn/jetty-alpn-openjdk8-client/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Client b/jetty-alpn/jetty-alpn-openjdk8-client/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Client
new file mode 100644
index 00000000000..de5d9cd9ec4
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-openjdk8-client/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Client
@@ -0,0 +1 @@
+org.eclipse.jetty.alpn.java.client.OpenJDK8ClientALPNProcessor
diff --git a/jetty-alpn/jetty-alpn-openjdk8-client/src/test/java/org/eclipse/jetty/alpn/java/client/OpenJDK8HTTP2Client.java b/jetty-alpn/jetty-alpn-openjdk8-client/src/test/java/org/eclipse/jetty/alpn/java/client/OpenJDK8HTTP2Client.java
new file mode 100644
index 00000000000..24e51849039
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-openjdk8-client/src/test/java/org/eclipse/jetty/alpn/java/client/OpenJDK8HTTP2Client.java
@@ -0,0 +1,85 @@
+//
+// ========================================================================
+// 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.
+// ========================================================================
+//
+
+package org.eclipse.jetty.alpn.java.client;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpURI;
+import org.eclipse.jetty.http.HttpVersion;
+import org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.http2.api.Session;
+import org.eclipse.jetty.http2.api.Stream;
+import org.eclipse.jetty.http2.client.HTTP2Client;
+import org.eclipse.jetty.http2.frames.DataFrame;
+import org.eclipse.jetty.http2.frames.HeadersFrame;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.FuturePromise;
+import org.eclipse.jetty.util.Jetty;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+public class OpenJDK8HTTP2Client
+{
+ public static void main(String[] args) throws Exception
+ {
+ HTTP2Client client = new HTTP2Client();
+ SslContextFactory sslContextFactory = new SslContextFactory();
+ client.addBean(sslContextFactory);
+ client.start();
+
+ String host = "webtide.com";
+ int port = 443;
+
+ FuturePromise sessionPromise = new FuturePromise<>();
+ client.connect(sslContextFactory, new InetSocketAddress(host, port), new Session.Listener.Adapter(), sessionPromise);
+ Session session = sessionPromise.get(5, TimeUnit.SECONDS);
+
+ HttpFields requestFields = new HttpFields();
+ requestFields.put("User-Agent", client.getClass().getName() + "/" + Jetty.VERSION);
+ MetaData.Request metaData = new MetaData.Request("GET", new HttpURI("https://" + host + ":" + port + "/"), HttpVersion.HTTP_2, requestFields);
+ HeadersFrame headersFrame = new HeadersFrame(metaData, null, true);
+ CountDownLatch latch = new CountDownLatch(1);
+ session.newStream(headersFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
+ {
+ @Override
+ public void onHeaders(Stream stream, HeadersFrame frame)
+ {
+ System.err.println(frame);
+ if (frame.isEndStream())
+ latch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataFrame frame, Callback callback)
+ {
+ System.err.println(frame);
+ callback.succeeded();
+ if (frame.isEndStream())
+ latch.countDown();
+ }
+ });
+
+ latch.await(5, TimeUnit.SECONDS);
+
+ client.stop();
+ }
+}
diff --git a/jetty-alpn/jetty-alpn-openjdk8-client/src/test/resources/jetty-logging.properties b/jetty-alpn/jetty-alpn-openjdk8-client/src/test/resources/jetty-logging.properties
new file mode 100644
index 00000000000..d96a696f82e
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-openjdk8-client/src/test/resources/jetty-logging.properties
@@ -0,0 +1,2 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+#org.eclipse.jetty.LEVEL=DEBUG
diff --git a/jetty-alpn/jetty-alpn-openjdk8-server/pom.xml b/jetty-alpn/jetty-alpn-openjdk8-server/pom.xml
new file mode 100644
index 00000000000..264fabe3457
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-openjdk8-server/pom.xml
@@ -0,0 +1,43 @@
+
+
+
+ org.eclipse.jetty
+ jetty-alpn-parent
+ 10.0.0-SNAPSHOT
+
+
+ 4.0.0
+ jetty-alpn-openjdk8-server
+ Jetty :: ALPN :: OpenJDK8 Server Implementation
+
+
+ ${project.groupId}.alpn.conscrypt.server
+
+
+
+
+ org.eclipse.jetty.alpn
+ alpn-api
+ ${alpn.api.version}
+ provided
+
+
+ org.eclipse.jetty
+ jetty-alpn-server
+ ${project.version}
+
+
+ org.eclipse.jetty
+ jetty-io
+ ${project.version}
+
+
+
+ org.eclipse.jetty.http2
+ http2-server
+ ${project.version}
+ test
+
+
+
diff --git a/jetty-alpn/jetty-alpn-openjdk8-server/src/main/java/org/eclipse/jetty/alpn/openjdk8/server/OpenJDK8ServerALPNProcessor.java b/jetty-alpn/jetty-alpn-openjdk8-server/src/main/java/org/eclipse/jetty/alpn/openjdk8/server/OpenJDK8ServerALPNProcessor.java
new file mode 100644
index 00000000000..6e25f9dc311
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-openjdk8-server/src/main/java/org/eclipse/jetty/alpn/openjdk8/server/OpenJDK8ServerALPNProcessor.java
@@ -0,0 +1,104 @@
+//
+// ========================================================================
+// 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.
+// ========================================================================
+//
+
+package org.eclipse.jetty.alpn.openjdk8.server;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+
+import org.eclipse.jetty.alpn.ALPN;
+import org.eclipse.jetty.alpn.server.ALPNServerConnection;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.ssl.ALPNProcessor;
+import org.eclipse.jetty.util.JavaVersion;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class OpenJDK8ServerALPNProcessor implements ALPNProcessor.Server
+{
+ private static final Logger LOG = Log.getLogger(OpenJDK8ServerALPNProcessor.class);
+
+ @Override
+ public void init()
+ {
+ if (JavaVersion.VERSION.getPlatform()!=8)
+ throw new IllegalStateException(this + " not applicable for java "+JavaVersion.VERSION);
+ if (ALPN.class.getClassLoader()!=null)
+ throw new IllegalStateException(ALPN.class.getName() + " must be on JVM boot classpath");
+ if (LOG.isDebugEnabled())
+ ALPN.debug = true;
+ }
+
+ @Override
+ public boolean appliesTo(SSLEngine sslEngine)
+ {
+ return sslEngine.getClass().getName().startsWith("sun.security.ssl.");
+ }
+
+ @Override
+ public void configure(SSLEngine sslEngine, Connection connection)
+ {
+ connection.addListener(new ALPNListener((ALPNServerConnection)connection));
+ }
+
+ private final class ALPNListener implements ALPN.ServerProvider, Connection.Listener
+ {
+ private final ALPNServerConnection alpnConnection;
+
+ private ALPNListener(ALPNServerConnection connection)
+ {
+ alpnConnection = connection;
+ }
+
+ @Override
+ public void onOpened(Connection connection)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("onOpened {}", alpnConnection);
+ ALPN.put(alpnConnection.getSSLEngine(), this);
+ }
+
+ @Override
+ public void onClosed(Connection connection)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("onClosed {}", alpnConnection);
+ ALPN.remove(alpnConnection.getSSLEngine());
+ }
+
+ @Override
+ public void unsupported()
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("unsupported {}", alpnConnection);
+ alpnConnection.select(Collections.emptyList());
+ }
+
+ @Override
+ public String select(List protocols) throws SSLException
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("select {} {}", alpnConnection, protocols);
+ alpnConnection.select(protocols);
+ return alpnConnection.getProtocol();
+ }
+ }
+}
diff --git a/jetty-alpn/jetty-alpn-openjdk8-server/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Server b/jetty-alpn/jetty-alpn-openjdk8-server/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Server
new file mode 100644
index 00000000000..3a2141ba0e8
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-openjdk8-server/src/main/resources/META-INF/services/org.eclipse.jetty.io.ssl.ALPNProcessor$Server
@@ -0,0 +1 @@
+org.eclipse.jetty.alpn.openjdk8.server.OpenJDK8ServerALPNProcessor
diff --git a/jetty-alpn/jetty-alpn-openjdk8-server/src/test/java/org/eclipse/jetty/alpn/openjdk8/server/OpenJDK8HTTP2Server.java b/jetty-alpn/jetty-alpn-openjdk8-server/src/test/java/org/eclipse/jetty/alpn/openjdk8/server/OpenJDK8HTTP2Server.java
new file mode 100644
index 00000000000..3efbd4bdbc3
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-openjdk8-server/src/test/java/org/eclipse/jetty/alpn/openjdk8/server/OpenJDK8HTTP2Server.java
@@ -0,0 +1,66 @@
+//
+// ========================================================================
+// 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.
+// ========================================================================
+//
+
+package org.eclipse.jetty.alpn.openjdk8.server;
+
+import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
+import org.eclipse.jetty.http2.HTTP2Cipher;
+import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.SecureRequestCustomizer;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.SslConnectionFactory;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+/**
+ * Test server that verifies that the JDK 8 ALPN mechanism works.
+ */
+public class OpenJDK8HTTP2Server
+{
+ public static void main(String... args) throws Exception
+ {
+ Server server = new Server();
+
+ HttpConfiguration httpsConfig = new HttpConfiguration();
+ httpsConfig.setSecureScheme("https");
+ httpsConfig.setSecurePort(8443);
+ httpsConfig.setSendXPoweredBy(true);
+ httpsConfig.setSendServerVersion(true);
+ httpsConfig.addCustomizer(new SecureRequestCustomizer());
+
+ SslContextFactory sslContextFactory = new SslContextFactory();
+ sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks");
+ sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4");
+ sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g");
+ sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
+
+ HttpConnectionFactory http = new HttpConnectionFactory(httpsConfig);
+ HTTP2ServerConnectionFactory h2 = new HTTP2ServerConnectionFactory(httpsConfig);
+ ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory();
+ alpn.setDefaultProtocol(http.getProtocol());
+ SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, alpn.getProtocol());
+
+ ServerConnector http2Connector = new ServerConnector(server, ssl, alpn, h2, http);
+ http2Connector.setPort(8443);
+ server.addConnector(http2Connector);
+
+ server.start();
+ }
+}
diff --git a/jetty-alpn/jetty-alpn-openjdk8-server/src/test/resources/jetty-logging.properties b/jetty-alpn/jetty-alpn-openjdk8-server/src/test/resources/jetty-logging.properties
new file mode 100644
index 00000000000..c391f84e35b
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-openjdk8-server/src/test/resources/jetty-logging.properties
@@ -0,0 +1,3 @@
+org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
+#org.eclipse.jetty.LEVEL=DEBUG
+#org.eclipse.jetty.alpn.LEVEL=DEBUG
diff --git a/jetty-alpn/jetty-alpn-openjdk8-server/src/test/resources/keystore.jks b/jetty-alpn/jetty-alpn-openjdk8-server/src/test/resources/keystore.jks
new file mode 100644
index 00000000000..d6592f95ee9
Binary files /dev/null and b/jetty-alpn/jetty-alpn-openjdk8-server/src/test/resources/keystore.jks differ
diff --git a/jetty-alpn/jetty-alpn-server/pom.xml b/jetty-alpn/jetty-alpn-server/pom.xml
index 4cc144a4a16..0fc6f0f3712 100644
--- a/jetty-alpn/jetty-alpn-server/pom.xml
+++ b/jetty-alpn/jetty-alpn-server/pom.xml
@@ -59,12 +59,6 @@
jetty-server${project.version}
-
- org.eclipse.jetty.alpn
- alpn-api
- ${alpn.api.version}
- provided
- org.eclipse.jetty.toolchainjetty-test-helper
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/etc/jetty-alpn.xml b/jetty-alpn/jetty-alpn-server/src/main/config/etc/jetty-alpn.xml
index f1288e6c056..e577c2baa44 100644
--- a/jetty-alpn/jetty-alpn-server/src/main/config/etc/jetty-alpn.xml
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/etc/jetty-alpn.xml
@@ -15,18 +15,9 @@
-
-
-
-
-
-
+
+
-
-
-
-
-
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl.mod
new file mode 100644
index 00000000000..d726e163d24
--- /dev/null
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl.mod
@@ -0,0 +1,5 @@
+[description]
+Selects an ALPN (Application Layer Protocol Negotiation) implementation by java version.
+
+[depend]
+alpn-impl/alpn-${java.version.platform}
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-8.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-8.mod
index a591d419f89..6ace07a2e6b 100644
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-8.mod
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn-impl/alpn-8.mod
@@ -17,6 +17,9 @@ specific version of Java.
[depend]
alpn-impl/alpn-${java.version}
+[lib]
+lib/jetty-alpn-openjdk8-server-${jetty.version}.jar
+
[files]
lib/
lib/alpn/
diff --git a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn.mod b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn.mod
index e25948f4bc8..cb02222ed89 100644
--- a/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn.mod
+++ b/jetty-alpn/jetty-alpn-server/src/main/config/modules/alpn.mod
@@ -3,7 +3,7 @@ Enables the ALPN (Application Layer Protocol Negotiation) TLS extension.
[depend]
ssl
-alpn-impl/alpn-${java.version.platform}
+alpn-impl
[lib]
lib/jetty-alpn-client-${jetty.version}.jar
@@ -21,6 +21,3 @@ etc/jetty-alpn.xml
## Specifies what protocol to use when negotiation fails.
# jetty.alpn.defaultProtocol=http/1.1
-## ALPN debug logging on System.err
-# jetty.alpn.debug=false
-
diff --git a/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnection.java b/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnection.java
index f3e5835c33b..83f819f62fb 100644
--- a/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnection.java
+++ b/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnection.java
@@ -24,7 +24,6 @@ import java.util.List;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLSession;
-import org.eclipse.jetty.alpn.ALPN;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.Connector;
@@ -32,24 +31,21 @@ import org.eclipse.jetty.server.NegotiatingServerConnection;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
-public class ALPNServerConnection extends NegotiatingServerConnection implements ALPN.ServerProvider
+public class ALPNServerConnection extends NegotiatingServerConnection
{
private static final Logger LOG = Log.getLogger(ALPNServerConnection.class);
public ALPNServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List protocols, String defaultProtocol)
{
super(connector, endPoint, engine, protocols, defaultProtocol);
- ALPN.put(engine, this);
}
- @Override
public void unsupported()
{
select(Collections.emptyList());
}
-
- @Override
- public String select(List clientProtocols)
+
+ public void select(List clientProtocols)
{
SSLEngine sslEngine = getSSLEngine();
List serverProtocols = getProtocols();
@@ -70,7 +66,7 @@ public class ALPNServerConnection extends NegotiatingServerConnection implements
if (factory instanceof CipherDiscriminator && !((CipherDiscriminator)factory).isAcceptable(serverProtocol, tlsProtocol, tlsCipher))
{
if (LOG.isDebugEnabled())
- LOG.debug("{} protocol {} not acceptable to {} for {}/{}", this, serverProtocol, factory, tlsProtocol, tlsCipher);
+ LOG.debug("Protocol {} not acceptable to {} for {}/{} on {}", serverProtocol, factory, tlsProtocol, tlsCipher, getEndPoint());
continue;
}
@@ -87,21 +83,12 @@ public class ALPNServerConnection extends NegotiatingServerConnection implements
else
{
if (LOG.isDebugEnabled())
- LOG.debug("{} could not negotiate protocol among client{} and server{}", this, clientProtocols, serverProtocols);
+ LOG.debug("Could not negotiate protocol from client{} and server{} on {}", clientProtocols, serverProtocols, getEndPoint());
throw new IllegalStateException();
}
}
if (LOG.isDebugEnabled())
- LOG.debug("{} protocol selected {} among client{} and server{}", this, negotiated, clientProtocols, serverProtocols);
+ LOG.debug("Protocol selected {} from client{} and server{} on {}", negotiated, clientProtocols, serverProtocols, getEndPoint());
setProtocol(negotiated);
- ALPN.remove(sslEngine);
- return negotiated;
- }
-
- @Override
- public void close()
- {
- ALPN.remove(getSSLEngine());
- super.close();
}
}
diff --git a/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnectionFactory.java b/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnectionFactory.java
index ccf886a73ef..56135bd008d 100644
--- a/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnectionFactory.java
+++ b/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/ALPNServerConnectionFactory.java
@@ -18,6 +18,8 @@
package org.eclipse.jetty.alpn.server;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceLoader;
@@ -26,52 +28,82 @@ import javax.net.ssl.SSLEngine;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.io.ssl.ALPNProcessor;
-import org.eclipse.jetty.io.ssl.SslHandshakeListener;
+import org.eclipse.jetty.io.ssl.ALPNProcessor.Server;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.NegotiatingServerConnectionFactory;
import org.eclipse.jetty.util.annotation.Name;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
-public class ALPNServerConnectionFactory extends NegotiatingServerConnectionFactory implements SslHandshakeListener
+public class ALPNServerConnectionFactory extends NegotiatingServerConnectionFactory
{
- private final ALPNProcessor.Server alpnProcessor;
+ private static final Logger LOG = Log.getLogger(ALPNServerConnectionFactory.class);
- public ALPNServerConnectionFactory(String protocols)
+ private final List processors = new ArrayList<>();
+
+ public ALPNServerConnectionFactory(@Name("protocols") String protocols)
{
this(protocols.trim().split(",", 0));
}
-
+
public ALPNServerConnectionFactory(@Name("protocols") String... protocols)
{
super("alpn", protocols);
- checkProtocolNegotiationAvailable();
- Iterator processors = ServiceLoader.load(ALPNProcessor.Server.class).iterator();
- alpnProcessor = processors.hasNext() ? processors.next() : ALPNProcessor.Server.NOOP;
- }
- public ALPNProcessor.Server getALPNProcessor()
- {
- return alpnProcessor;
+ IllegalStateException failure = new IllegalStateException("No Server ALPNProcessors!");
+ // Use a for loop on iterator so load exceptions can be caught and ignored
+ for (Iterator i = ServiceLoader.load(Server.class).iterator(); i.hasNext();)
+ {
+ Server processor;
+ try
+ {
+ processor = i.next();
+ }
+ catch(Throwable x)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug(x);
+ failure.addSuppressed(x);
+ continue;
+ }
+
+ try
+ {
+ processor.init();
+ processors.add(processor);
+ }
+ catch (Throwable x)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Could not initialize " + processor, x);
+ failure.addSuppressed(x);
+ }
+ }
+
+ if (LOG.isDebugEnabled())
+ {
+ LOG.debug("protocols: {}", Arrays.asList(protocols));
+ LOG.debug("processors: {}", processors);
+ }
+
+ if (processors.isEmpty())
+ throw failure;
}
@Override
protected AbstractConnection newServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List protocols, String defaultProtocol)
{
- getALPNProcessor().configure(engine);
- return new ALPNServerConnection(connector, endPoint, engine, protocols, defaultProtocol);
- }
-
- @Override
- public void handshakeSucceeded(Event event)
- {
- if (alpnProcessor instanceof SslHandshakeListener)
- ((SslHandshakeListener)alpnProcessor).handshakeSucceeded(event);
- }
-
- @Override
- public void handshakeFailed(Event event, Throwable failure)
- {
- if (alpnProcessor instanceof SslHandshakeListener)
- ((SslHandshakeListener)alpnProcessor).handshakeFailed(event, failure);
+ for (Server processor : processors)
+ {
+ if (processor.appliesTo(engine))
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("{} for {} on {}", processor, engine, endPoint);
+ ALPNServerConnection connection = new ALPNServerConnection(connector, endPoint, engine, protocols, defaultProtocol);
+ processor.configure(engine, connection);
+ return connection;
+ }
+ }
+ throw new IllegalStateException("No ALPNProcessor for " + engine);
}
}
diff --git a/jetty-alpn/pom.xml b/jetty-alpn/pom.xml
index 53e9669cbef..941b382d7e2 100644
--- a/jetty-alpn/pom.xml
+++ b/jetty-alpn/pom.xml
@@ -13,6 +13,10 @@
jetty-alpn-serverjetty-alpn-client
+ jetty-alpn-openjdk8-server
+ jetty-alpn-openjdk8-client
+ jetty-alpn-conscrypt-server
+ jetty-alpn-conscrypt-client
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java
index 2ec5516e77d..13f9b36b77d 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java
@@ -24,16 +24,23 @@ import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
+import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.MultiException;
+import org.eclipse.jetty.util.MultiReleaseJarFile;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
@@ -45,7 +52,6 @@ import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
-
/**
* AnnotationParser
*
@@ -68,10 +74,29 @@ public class AnnotationParser
{
private static final Logger LOG = Log.getLogger(AnnotationParser.class);
- protected Set _parsedClassNames = ConcurrentHashMap.newKeySet();
-
+ private static final int JVM_MAJOR_VER;
protected static int ASM_OPCODE_VERSION = Opcodes.ASM5; //compatibility of api
-
+
+ protected Map> _parsedClassNames = new ConcurrentHashMap<>();
+
+ static
+ {
+ // Determine JVM spec version
+ // Using guidance from http://openjdk.java.net/jeps/223
+ String jvmSpecVer = System.getProperty("java.vm.specification.version");
+
+ if (jvmSpecVer.indexOf('.') >= 0)
+ {
+ // Old spec version (Java 1.8 and older)
+ String parts[] = jvmSpecVer.split("\\.");
+ JVM_MAJOR_VER = Integer.parseInt(parts[1]);
+ }
+ else
+ {
+ // Newer spec version (Java 9+)
+ JVM_MAJOR_VER = Integer.parseInt(jvmSpecVer);
+ }
+ }
/**
* Convert internal name to simple name
@@ -110,12 +135,8 @@ public class AnnotationParser
return normalList;
}
-
/**
- * ClassInfo
- *
* Immutable information gathered by parsing class header.
- *
*/
public class ClassInfo
{
@@ -175,10 +196,7 @@ public class AnnotationParser
}
}
-
/**
- * MethodInfo
- *
* Immutable information gathered by parsing a method on a class.
*/
public class MethodInfo
@@ -231,14 +249,9 @@ public class AnnotationParser
return _exceptions;
}
}
-
-
-
+
/**
- * FieldInfo
- *
* Immutable information gathered by parsing a field on a class.
- *
*/
public class FieldInfo
{
@@ -292,8 +305,6 @@ public class AnnotationParser
}
/**
- * Handler
- *
* Signature for all handlers that respond to parsing class files.
*/
public static interface Handler
@@ -307,13 +318,10 @@ public class AnnotationParser
}
/**
- * AbstractHandler
- *
* Convenience base class to provide no-ops for all Handler methods.
*/
public static abstract class AbstractHandler implements Handler
{
-
@Override
public void handle(ClassInfo classInfo)
{
@@ -347,15 +355,11 @@ public class AnnotationParser
@Override
public void handle(FieldInfo info, String annotationName)
{
- // no-op
- }
+ // no-op
+ }
}
-
-
/**
- * MyMethodVisitor
- *
* ASM Visitor for parsing a method. We are only interested in the annotations on methods.
*/
public class MyMethodVisitor extends MethodVisitor
@@ -376,11 +380,8 @@ public class AnnotationParser
_mi = new MethodInfo(classInfo, name, access, methodDesc,signature, exceptions);
}
-
/**
* We are only interested in finding the annotations on methods.
- *
- * @see org.objectweb.asm.MethodVisitor#visitAnnotation(java.lang.String, boolean)
*/
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
@@ -392,21 +393,15 @@ public class AnnotationParser
}
}
-
-
/**
- * MyFieldVisitor
- *
- * An ASM visitor for parsing Fields.
+ * An ASM visitor for parsing Fields.
* We are only interested in visiting annotations on Fields.
- *
*/
public class MyFieldVisitor extends FieldVisitor
{
final FieldInfo _fieldInfo;
final Set extends Handler> _handlers;
-
-
+
public MyFieldVisitor(final Set extends Handler> handlers,
final ClassInfo classInfo,
final int access,
@@ -420,11 +415,8 @@ public class AnnotationParser
_fieldInfo = new FieldInfo(classInfo, fieldName, access, fieldType, signature, value);
}
-
/**
* Parse an annotation found on a Field.
- *
- * @see org.objectweb.asm.FieldVisitor#visitAnnotation(java.lang.String, boolean)
*/
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
@@ -437,17 +429,11 @@ public class AnnotationParser
}
}
-
-
-
/**
- * MyClassVisitor
- *
* ASM visitor for a class.
*/
public class MyClassVisitor extends ClassVisitor
{
-
final Resource _containingResource;
final Set extends Handler> _handlers;
ClassInfo _ci;
@@ -459,7 +445,6 @@ public class AnnotationParser
_containingResource = containingResource;
}
-
@Override
public void visit (final int version,
final int access,
@@ -469,18 +454,12 @@ public class AnnotationParser
final String[] interfaces)
{
_ci = new ClassInfo(_containingResource, normalize(name), version, access, signature, normalize(superName), normalize(interfaces));
-
- _parsedClassNames.add(_ci.getClassName());
-
for (Handler h:_handlers)
h.handle(_ci);
}
-
/**
* Visit an annotation on a Class
- *
- * @see org.objectweb.asm.ClassVisitor#visitAnnotation(java.lang.String, boolean)
*/
@Override
public AnnotationVisitor visitAnnotation (String desc, boolean visible)
@@ -488,15 +467,11 @@ public class AnnotationParser
String annotationName = normalize(desc);
for (Handler h : _handlers)
h.handle(_ci, annotationName);
-
return null;
}
-
/**
* Visit a method to extract its annotations
- *
- * @see org.objectweb.asm.ClassVisitor#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
*/
@Override
public MethodVisitor visitMethod (final int access,
@@ -505,14 +480,11 @@ public class AnnotationParser
final String signature,
final String[] exceptions)
{
-
return new MyMethodVisitor(_handlers, _ci, access, name, methodDesc, signature, exceptions);
}
/**
* Visit a field to extract its annotations
- *
- * @see org.objectweb.asm.ClassVisitor#visitField(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object)
*/
@Override
public FieldVisitor visitField (final int access,
@@ -525,20 +497,41 @@ public class AnnotationParser
}
}
-
-
/**
- * True if the class has already been processed, false otherwise
- * @param className the classname
- * @return true if class was parsed, false if not
+ * Add a class as having been parsed.
+ *
+ * @param classname the name of the class
+ * @param location the fully qualified location of the class
*/
- public boolean isParsed (String className)
+ public void addParsedClass (String classname, Resource location)
{
- return _parsedClassNames.contains(className);
+ List list = new ArrayList<>(1);
+ if (location != null)
+ list.add(location.toString());
+
+ List existing = _parsedClassNames.putIfAbsent(classname, list);
+ if (existing != null)
+ {
+ existing.addAll(list);
+ LOG.warn("{} scanned from multiple locations: {}", classname, existing);
+ }
+ }
+
+ /**
+ * Get the locations of the given classname. There may be more than one
+ * location if there are duplicates of the same class.
+ *
+ * @param classname the name of the class
+ * @return an immutable list of locations
+ */
+ public List getParsedLocations (String classname)
+ {
+ List list = _parsedClassNames.get(classname);
+ if (list == null)
+ return Collections.emptyList();
+ return Collections.unmodifiableList(list);
}
-
-
/**
* Parse a given class
*
@@ -546,29 +539,25 @@ public class AnnotationParser
* @param className the class name to parse
* @throws Exception if unable to parse
*/
- public void parse (Set extends Handler> handlers, String className)
- throws Exception
+ public void parse (Set extends Handler> handlers, String className) throws Exception
{
if (className == null)
return;
- if (!isParsed(className))
+ String tmp = className;
+ className = className.replace('.', '/')+".class";
+ URL resource = Loader.getResource(className);
+ if (resource!= null)
{
- className = className.replace('.', '/')+".class";
- URL resource = Loader.getResource(className);
- if (resource!= null)
+ Resource r = Resource.newResource(resource);
+ addParsedClass(tmp, r);
+ try (InputStream is = r.getInputStream())
{
- Resource r = Resource.newResource(resource);
- try (InputStream is = r.getInputStream())
- {
- scanClass(handlers, null, is);
- }
+ scanClass(handlers, null, is);
}
}
}
-
-
/**
* Parse the given class, optionally walking its inheritance hierarchy
*
@@ -577,27 +566,23 @@ public class AnnotationParser
* @param visitSuperClasses if true, also visit super classes for parse
* @throws Exception if unable to parse class
*/
- public void parse (Set extends Handler> handlers, Class> clazz, boolean visitSuperClasses)
- throws Exception
+ public void parse (Set extends Handler> handlers, Class> clazz, boolean visitSuperClasses) throws Exception
{
Class> cz = clazz;
while (cz != Object.class)
{
- if (!isParsed(cz.getName()))
+ String nameAsResource = cz.getName().replace('.', '/')+".class";
+ URL resource = Loader.getResource(nameAsResource);
+ if (resource!= null)
{
- String nameAsResource = cz.getName().replace('.', '/')+".class";
- URL resource = Loader.getResource(nameAsResource);
- if (resource!= null)
+ Resource r = Resource.newResource(resource);
+ addParsedClass(clazz.getName(), r);
+ try (InputStream is = r.getInputStream())
{
- Resource r = Resource.newResource(resource);
- try (InputStream is = r.getInputStream())
- {
- scanClass(handlers, null, is);
- }
+ scanClass(handlers, null, is);
}
}
-
if (visitSuperClasses)
cz = cz.getSuperclass();
else
@@ -605,8 +590,6 @@ public class AnnotationParser
}
}
-
-
/**
* Parse the given classes
*
@@ -614,8 +597,7 @@ public class AnnotationParser
* @param classNames the class name
* @throws Exception if unable to parse
*/
- public void parse (Set extends Handler> handlers, String[] classNames)
- throws Exception
+ public void parse (Set extends Handler> handlers, String[] classNames) throws Exception
{
if (classNames == null)
return;
@@ -623,7 +605,6 @@ public class AnnotationParser
parse(handlers, Arrays.asList(classNames));
}
-
/**
* Parse the given classes
*
@@ -631,26 +612,24 @@ public class AnnotationParser
* @param classNames the class names
* @throws Exception if unable to parse
*/
- public void parse (Set extends Handler> handlers, List classNames)
- throws Exception
+ public void parse (Set extends Handler> handlers, List classNames) throws Exception
{
MultiException me = new MultiException();
-
+
for (String s:classNames)
{
try
{
- if (!isParsed(s))
+ String name = s;
+ s = s.replace('.', '/')+".class";
+ URL resource = Loader.getResource(s);
+ if (resource!= null)
{
- s = s.replace('.', '/')+".class";
- URL resource = Loader.getResource(s);
- if (resource!= null)
+ Resource r = Resource.newResource(resource);
+ addParsedClass(name, r);
+ try (InputStream is = r.getInputStream())
{
- Resource r = Resource.newResource(resource);
- try (InputStream is = r.getInputStream())
- {
- scanClass(handlers, null, is);
- }
+ scanClass(handlers, null, is);
}
}
}
@@ -662,67 +641,65 @@ public class AnnotationParser
me.ifExceptionThrow();
}
-
/**
* Parse all classes in a directory
*
* @param handlers the set of handlers to look for classes in
- * @param dir the resource directory to look for classes
+ * @param root the resource directory to look for classes
* @throws Exception if unable to parse
*/
- protected void parseDir (Set extends Handler> handlers, Resource dir)
- throws Exception
+ protected void parseDir (Set extends Handler> handlers, Resource root) throws Exception
{
- // skip dirs whose name start with . (ie hidden)
- if (!dir.isDirectory() || !dir.exists() || dir.getName().startsWith("."))
+ if (!root.isDirectory() || !root.exists() || root.getName().startsWith("."))
return;
- if (LOG.isDebugEnabled()) {LOG.debug("Scanning dir {}", dir);};
-
- MultiException me = new MultiException();
+ if (LOG.isDebugEnabled())
+ LOG.debug("Scanning dir {}", root);
- String[] files=dir.list();
- for (int f=0;files!=null && f resources = root.getAllResources();
+ if (resources != null)
{
- Resource res = dir.addPath(files[f]);
- if (res.isDirectory())
- parseDir(handlers, res);
- else
+ for (Resource r:resources)
{
- //we've already verified the directories, so just verify the class file name
- File file = res.getFile();
+ if (r.isDirectory())
+ continue;
+
+ File file = r.getFile();
if (isValidClassFileName((file==null?null:file.getName())))
{
+ Path classpath = rootFile.toPath().relativize(file.toPath());
+ String str = classpath.toString();
+ str = str.substring(0, str.lastIndexOf(".class")).replace('/', '.').replace('\\', '.');
+
try
{
- String name = res.getName();
- if (!isParsed(name))
+ if (LOG.isDebugEnabled())
+ LOG.debug("Scanning class {}", r);
+ addParsedClass(str, r);
+ try (InputStream is=r.getInputStream())
{
- Resource r = Resource.newResource(res.getURL());
- if (LOG.isDebugEnabled()) {LOG.debug("Scanning class {}", r);};
- try (InputStream is=r.getInputStream())
- {
- scanClass(handlers, dir, is);
- }
+ scanClass(handlers, Resource.newResource(file.getParentFile()), is);
}
}
catch (Exception ex)
{
- if (LOG.isDebugEnabled()) LOG.debug("Error scanning file "+files[f], ex);
- me.add(new RuntimeException("Error scanning file "+files[f],ex));
+ if (LOG.isDebugEnabled()) LOG.debug("Error scanning file "+file, ex);
+ me.add(new RuntimeException("Error scanning file "+file,ex));
}
}
else
{
- if (LOG.isDebugEnabled()) LOG.debug("Skipping scan on invalid file {}", res);
+ if (LOG.isDebugEnabled()) LOG.debug("Skipping scan on invalid file {}", file);
}
}
}
-
+
me.ifExceptionThrow();
}
-
/**
* Parse classes in the supplied classloader.
* Only class files in jar files will be scanned.
@@ -733,39 +710,12 @@ public class AnnotationParser
* @param nullInclusive if true, an empty pattern means all names match, if false, none match
* @throws Exception if unable to parse
*/
- public void parse (final Set extends Handler> handlers, ClassLoader loader, boolean visitParents, boolean nullInclusive)
- throws Exception
+ @Deprecated
+ public void parse (final Set extends Handler> handlers, ClassLoader loader, boolean visitParents, boolean nullInclusive) throws Exception
{
- if (loader==null)
- return;
-
- if (!(loader instanceof URLClassLoader))
- return; //can't extract classes?
-
- final MultiException me = new MultiException();
-
- JarScanner scanner = new JarScanner()
- {
- @Override
- public void processEntry(URI jarUri, JarEntry entry)
- {
- try
- {
- parseJarEntry(handlers, Resource.newResource(jarUri), entry);
- }
- catch (Exception e)
- {
- me.add(new RuntimeException("Error parsing entry "+entry.getName()+" from jar "+ jarUri, e));
- }
- }
-
- };
-
- scanner.scan(null, loader, nullInclusive, visitParents);
- me.ifExceptionThrow();
+ throw new UnsupportedOperationException();
}
-
/**
* Parse classes in the supplied uris.
*
@@ -773,8 +723,7 @@ public class AnnotationParser
* @param uris the uris for the jars
* @throws Exception if unable to parse
*/
- public void parse (final Set extends Handler> handlers, final URI[] uris)
- throws Exception
+ public void parse (final Set extends Handler> handlers, final URI[] uris) throws Exception
{
if (uris==null)
return;
@@ -802,8 +751,7 @@ public class AnnotationParser
* @param uri the uri for the jar
* @throws Exception if unable to parse
*/
- public void parse (final Set extends Handler> handlers, URI uri)
- throws Exception
+ public void parse (final Set extends Handler> handlers, URI uri) throws Exception
{
if (uri == null)
return;
@@ -811,7 +759,6 @@ public class AnnotationParser
parse (handlers, Resource.newResource(uri));
}
-
/**
* Parse a resource
*
@@ -819,8 +766,7 @@ public class AnnotationParser
* @param r the resource to parse
* @throws Exception if unable to parse
*/
- public void parse (final Set extends Handler> handlers, Resource r)
- throws Exception
+ public void parse (final Set extends Handler> handlers, Resource r) throws Exception
{
if (r == null)
return;
@@ -850,9 +796,6 @@ public class AnnotationParser
if (LOG.isDebugEnabled()) LOG.warn("Resource not scannable for classes: {}", r);
}
-
-
-
/**
* Parse a resource that is a jar file.
*
@@ -860,47 +803,31 @@ public class AnnotationParser
* @param jarResource the jar resource to parse
* @throws Exception if unable to parse
*/
- protected void parseJar (Set extends Handler> handlers, Resource jarResource)
- throws Exception
+ protected void parseJar (Set extends Handler> handlers, Resource jarResource) throws Exception
{
if (jarResource == null)
return;
if (jarResource.toString().endsWith(".jar"))
{
- if (LOG.isDebugEnabled()) {LOG.debug("Scanning jar {}", jarResource);};
-
- //treat it as a jar that we need to open and scan all entries from
- InputStream in = jarResource.getInputStream();
- if (in==null)
- return;
+ if (LOG.isDebugEnabled())
+ LOG.debug("Scanning jar {}", jarResource);
MultiException me = new MultiException();
- JarInputStream jar_in = new JarInputStream(in);
- try
- {
- JarEntry entry = jar_in.getNextJarEntry();
- while (entry!=null)
- {
- try
- {
- parseJarEntry(handlers, jarResource, entry);
- }
- catch (Exception e)
- {
- me.add(new RuntimeException("Error scanning entry "+entry.getName()+" from jar "+jarResource, e));
- }
- entry = jar_in.getNextJarEntry();
+ // TODO do not force version 8 once ASM can scan 9
+ MultiReleaseJarFile jarFile = new MultiReleaseJarFile(jarResource.getFile(),8,false);
+ jarFile.stream().forEach(e->
+ {
+ try
+ {
+ parseJarEntry(handlers, jarResource, e);
}
- }
- catch (Exception e)
- {
- me.add(new RuntimeException("Error scanning jar "+jarResource, e));
- }
- finally
- {
- jar_in.close();
- }
+ catch (Exception ex)
+ {
+ me.add(new RuntimeException("Error scanning entry " + e.getName() + " from jar " + jarResource, ex));
+ }
+ });
+
me.ifExceptionThrow();
}
@@ -910,12 +837,10 @@ public class AnnotationParser
* Parse a single entry in a jar file
*
* @param handlers the handlers to look for classes in
- * @param jar the jar resource to parse
- * @param entry the entry in the jar resource to parse
+ * @param entry the entry in the potentially MultiRelease jar resource to parse
* @throws Exception if unable to parse
*/
- protected void parseJarEntry (Set extends Handler> handlers, Resource jar, JarEntry entry)
- throws Exception
+ protected void parseJarEntry (Set extends Handler> handlers, Resource jar, MultiReleaseJarFile.VersionedJarEntry entry) throws Exception
{
if (jar == null || entry == null)
return;
@@ -930,20 +855,15 @@ public class AnnotationParser
if (isValidClassFileName(name) && isValidClassFilePath(name))
{
String shortName = name.replace('/', '.').substring(0,name.length()-6);
-
- if (!isParsed(shortName))
+ addParsedClass(shortName, Resource.newResource("jar:"+jar.getURI()+"!/"+entry.getNameInJar()));
+ if (LOG.isDebugEnabled())
+ LOG.debug("Scanning class from jar {}!/{}", jar, entry);
+ try (InputStream is = entry.getInputStream())
{
- Resource clazz = Resource.newResource("jar:"+jar.getURI()+"!/"+name);
- if (LOG.isDebugEnabled()) {LOG.debug("Scanning class from jar {}", clazz);};
- try (InputStream is = clazz.getInputStream())
- {
- scanClass(handlers, jar, is);
- }
+ scanClass(handlers, jar, is);
}
}
}
-
-
/**
* Use ASM on a class
@@ -953,13 +873,20 @@ public class AnnotationParser
* @param is the input stream to parse
* @throws IOException if unable to parse
*/
- protected void scanClass (Set extends Handler> handlers, Resource containingResource, InputStream is)
- throws IOException
+ protected void scanClass (Set extends Handler> handlers, Resource containingResource, InputStream is) throws IOException
{
ClassReader reader = new ClassReader(is);
reader.accept(new MyClassVisitor(handlers, containingResource), ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES);
}
+ /**
+ * Remove any parsed class names.
+ */
+ public void resetParsedClasses ()
+ {
+ _parsedClassNames.clear();
+ }
+
/**
* Check that the given path represents a valid class file name.
* The check is fairly cursory, checking that:
@@ -968,8 +895,8 @@ public class AnnotationParser
*
it isn't a dot file or in a hidden directory
*
the name of the class at least begins with a valid identifier for a class name
*
- * @param name
- * @return
+ * @param name the class file name
+ * @return whether the class file name is valid
*/
private boolean isValidClassFileName (String name)
{
@@ -1003,13 +930,12 @@ public class AnnotationParser
return true;
}
-
-
+
/**
* Check that the given path does not contain hidden directories
*
- * @param path
- * @return
+ * @param path the class file path
+ * @return whether the class file path is valid
*/
private boolean isValidClassFilePath (String path)
{
@@ -1017,14 +943,13 @@ public class AnnotationParser
if (path == null || path.length()==0)
return false;
- //skip any classfiles that are in a hidden directory
+ // skip any classfiles that are in a hidden directory
if (path.startsWith(".") || path.contains("/."))
{
- if (LOG.isDebugEnabled()) LOG.debug("Contains hidden dirs: {}"+path);
+ if (LOG.isDebugEnabled()) LOG.debug("Contains hidden dirs: " + path);
return false;
}
return true;
}
}
-
diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/DeclareRolesAnnotationHandler.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/DeclareRolesAnnotationHandler.java
index 10c91074302..7f7cc18cd6c 100644
--- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/DeclareRolesAnnotationHandler.java
+++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/DeclareRolesAnnotationHandler.java
@@ -18,11 +18,15 @@
package org.eclipse.jetty.annotations;
+
import javax.annotation.security.DeclareRoles;
import javax.servlet.Servlet;
import org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler;
+import org.eclipse.jetty.security.ConstraintAware;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.webapp.WebAppContext;
/**
@@ -30,6 +34,7 @@ import org.eclipse.jetty.webapp.WebAppContext;
*/
public class DeclareRolesAnnotationHandler extends AbstractIntrospectableAnnotationHandler
{
+ private static final Logger LOG = Log.getLogger(DeclareRolesAnnotationHandler.class);
protected WebAppContext _context;
@@ -48,6 +53,12 @@ public class DeclareRolesAnnotationHandler extends AbstractIntrospectableAnnotat
if (!Servlet.class.isAssignableFrom(clazz))
return; //only applicable on javax.servlet.Servlet derivatives
+ if (!(_context.getSecurityHandler() instanceof ConstraintAware))
+ {
+ LOG.warn("SecurityHandler not ConstraintAware, skipping security annotation processing");
+ return;
+ }
+
DeclareRoles declareRoles = (DeclareRoles) clazz.getAnnotation(DeclareRoles.class);
if (declareRoles == null)
return;
diff --git a/jetty-annotations/src/test/java/org/acme/ClassOne.java b/jetty-annotations/src/test/java/org/acme/ClassOne.java
new file mode 100644
index 00000000000..29ea5c94600
--- /dev/null
+++ b/jetty-annotations/src/test/java/org/acme/ClassOne.java
@@ -0,0 +1,35 @@
+package org.acme;
+//
+// ========================================================================
+// 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.
+// ========================================================================
+//
+
+
+
+
+/**
+ * ClassOne
+ *
+ *
+ */
+public class ClassOne
+{
+
+ public void one()
+ {
+ }
+
+}
diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java
index 8ab3cfe5f9a..590947d3883 100644
--- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java
+++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java
@@ -29,6 +29,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.URI;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
@@ -44,6 +45,7 @@ import org.eclipse.jetty.toolchain.test.FS;
import org.eclipse.jetty.toolchain.test.IO;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.TestingDir;
+import org.eclipse.jetty.util.resource.Resource;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
@@ -163,6 +165,26 @@ public class TestAnnotationParser
// only the valid classes inside bad-classes.jar should be parsed. If any invalid classes are parsed and exception would be thrown here
}
+ @Test
+ public void testModuleInfoClassInJar() throws Exception
+ {
+ File badClassesJar = MavenTestingUtils.getTestResourceFile("jdk9/slf4j-api-1.8.0-alpha2.jar");
+ AnnotationParser parser = new AnnotationParser();
+ Set emptySet = Collections.emptySet();
+ parser.parse(emptySet, badClassesJar.toURI());
+ // Should throw no exceptions, and happily skip the module-info.class files
+ }
+
+ @Test
+ public void testJep238MultiReleaseInJar() throws Exception
+ {
+ File badClassesJar = MavenTestingUtils.getTestResourceFile("jdk9/log4j-api-2.9.0.jar");
+ AnnotationParser parser = new AnnotationParser();
+ Set emptySet = Collections.emptySet();
+ parser.parse(emptySet, badClassesJar.toURI());
+ // Should throw no exceptions, and skip the META-INF/versions/9/* files
+ }
+
@Test
public void testBasedirExclusion() throws Exception
{
@@ -189,6 +211,40 @@ public class TestAnnotationParser
// Validate
Assert.assertThat("Found Class", tracker.foundClasses, contains(ClassA.class.getName()));
}
+
+
+ @Test
+ public void testScanDuplicateClassesInJars() throws Exception
+ {
+ Resource testJar = Resource.newResource(MavenTestingUtils.getTestResourceFile("tinytest.jar"));
+ Resource testJar2 = Resource.newResource(MavenTestingUtils.getTestResourceFile("tinytest_copy.jar"));
+ AnnotationParser parser = new AnnotationParser();
+ Set emptySet = Collections.emptySet();
+ parser.parse(emptySet, testJar);
+ parser.parse(emptySet, testJar2);
+ List locations = parser.getParsedLocations("org.acme.ClassOne");
+ Assert.assertNotNull(locations);
+ Assert.assertEquals(2, locations.size());
+ Assert.assertTrue(!(locations.get(0).equals(locations.get(1))));
+ }
+
+
+ @Test
+ public void testScanDuplicateClasses() throws Exception
+ {
+ Resource testJar = Resource.newResource(MavenTestingUtils.getTestResourceFile("tinytest.jar"));
+ File testClasses = new File(MavenTestingUtils.getTargetDir(), "test-classes");
+ AnnotationParser parser = new AnnotationParser();
+ Set emptySet = Collections.emptySet();
+ parser.parse(emptySet, testJar);
+ parser.parse(emptySet, Resource.newResource(testClasses));
+ List locations = parser.getParsedLocations("org.acme.ClassOne");
+ Assert.assertNotNull(locations);
+ Assert.assertEquals(2, locations.size());
+ Assert.assertTrue(!(locations.get(0).equals(locations.get(1))));
+ }
+
+
private void copyClass(Class> clazz, File basedir) throws IOException
{
diff --git a/jetty-annotations/src/test/resources/jdk9/log4j-api-2.9.0.jar b/jetty-annotations/src/test/resources/jdk9/log4j-api-2.9.0.jar
new file mode 100644
index 00000000000..ab98d40e9a2
Binary files /dev/null and b/jetty-annotations/src/test/resources/jdk9/log4j-api-2.9.0.jar differ
diff --git a/jetty-annotations/src/test/resources/jdk9/slf4j-api-1.8.0-alpha2.jar b/jetty-annotations/src/test/resources/jdk9/slf4j-api-1.8.0-alpha2.jar
new file mode 100644
index 00000000000..7a2a9b2d8e4
Binary files /dev/null and b/jetty-annotations/src/test/resources/jdk9/slf4j-api-1.8.0-alpha2.jar differ
diff --git a/jetty-annotations/src/test/resources/tinytest.jar b/jetty-annotations/src/test/resources/tinytest.jar
new file mode 100644
index 00000000000..4041f0e8a1b
Binary files /dev/null and b/jetty-annotations/src/test/resources/tinytest.jar differ
diff --git a/jetty-annotations/src/test/resources/tinytest_copy.jar b/jetty-annotations/src/test/resources/tinytest_copy.jar
new file mode 100644
index 00000000000..db720508d0d
Binary files /dev/null and b/jetty-annotations/src/test/resources/tinytest_copy.jar differ
diff --git a/jetty-bom/pom.xml b/jetty-bom/pom.xml
index 5238f90728d..50e3f41f506 100644
--- a/jetty-bom/pom.xml
+++ b/jetty-bom/pom.xml
@@ -169,12 +169,12 @@
{@link HttpClient} provides an efficient, asynchronous, non-blocking implementation
@@ -118,33 +119,33 @@ public class HttpClient extends ContainerLifeCycle
private final ConcurrentMap destinations = new ConcurrentHashMap<>();
private final ProtocolHandlers handlers = new ProtocolHandlers();
private final List requestListeners = new ArrayList<>();
- private final AuthenticationStore authenticationStore = new HttpAuthenticationStore();
private final Set decoderFactories = new ContentDecoderFactorySet();
private final ProxyConfiguration proxyConfig = new ProxyConfiguration();
private final HttpClientTransport transport;
private final SslContextFactory sslContextFactory;
- private volatile CookieManager cookieManager;
- private volatile CookieStore cookieStore;
- private volatile Executor executor;
- private volatile ByteBufferPool byteBufferPool;
- private volatile Scheduler scheduler;
- private volatile SocketAddressResolver resolver;
- private volatile HttpField agentField = new HttpField(HttpHeader.USER_AGENT, "Jetty/" + Jetty.VERSION);
- private volatile boolean followRedirects = true;
- private volatile int maxConnectionsPerDestination = 64;
- private volatile int maxRequestsQueuedPerDestination = 1024;
- private volatile int requestBufferSize = 4096;
- private volatile int responseBufferSize = 16384;
- private volatile int maxRedirects = 8;
- private volatile SocketAddress bindAddress;
- private volatile long connectTimeout = 15000;
- private volatile long addressResolutionTimeout = 15000;
- private volatile long idleTimeout;
- private volatile boolean tcpNoDelay = true;
- private volatile boolean strictEventOrdering = false;
- private volatile HttpField encodingField;
- private volatile boolean removeIdleDestinations = false;
- private volatile boolean connectBlocking = false;
+ private AuthenticationStore authenticationStore = new HttpAuthenticationStore();
+ private CookieManager cookieManager;
+ private CookieStore cookieStore;
+ private Executor executor;
+ private ByteBufferPool byteBufferPool;
+ private Scheduler scheduler;
+ private SocketAddressResolver resolver;
+ private HttpField agentField = new HttpField(HttpHeader.USER_AGENT, "Jetty/" + Jetty.VERSION);
+ private boolean followRedirects = true;
+ private int maxConnectionsPerDestination = 64;
+ private int maxRequestsQueuedPerDestination = 1024;
+ private int requestBufferSize = 4096;
+ private int responseBufferSize = 16384;
+ private int maxRedirects = 8;
+ private SocketAddress bindAddress;
+ private long connectTimeout = 15000;
+ private long addressResolutionTimeout = 15000;
+ private long idleTimeout;
+ private boolean tcpNoDelay = true;
+ private boolean strictEventOrdering = false;
+ private HttpField encodingField;
+ private boolean removeIdleDestinations = false;
+ private boolean connectBlocking = false;
/**
* Creates a {@link HttpClient} instance that can perform requests to non-TLS destinations only
@@ -204,9 +205,12 @@ public class HttpClient extends ContainerLifeCycle
executor = threadPool;
}
addBean(executor);
-
+
if (byteBufferPool == null)
- byteBufferPool = new MappedByteBufferPool();
+ byteBufferPool = new MappedByteBufferPool(2048,
+ executor instanceof ThreadPool.SizedThreadPool
+ ? ((ThreadPool.SizedThreadPool)executor).getMaxThreads()/2
+ : Runtime.getRuntime().availableProcessors()*2);
addBean(byteBufferPool);
if (scheduler == null)
@@ -302,6 +306,14 @@ public class HttpClient extends ContainerLifeCycle
return authenticationStore;
}
+ /**
+ * @param authenticationStore the authentication store associated with this instance
+ */
+ public void setAuthenticationStore(AuthenticationStore authenticationStore)
+ {
+ this.authenticationStore = authenticationStore;
+ }
+
/**
* Returns a non thread-safe set of {@link ContentDecoder.Factory}s that can be modified before
* performing requests.
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpResponseAbortTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpResponseAbortTest.java
index cb2fa71af84..1dca5d058f6 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpResponseAbortTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpResponseAbortTest.java
@@ -29,9 +29,7 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.eclipse.jetty.client.api.Response;
-import org.eclipse.jetty.client.api.Result;
-import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.client.util.DeferredContentProvider;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
@@ -53,22 +51,11 @@ public class HttpResponseAbortTest extends AbstractHttpClientServerTest
final CountDownLatch latch = new CountDownLatch(1);
client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
- .onResponseBegin(new Response.BeginListener()
+ .onResponseBegin(response -> response.abort(new Exception()))
+ .send(result ->
{
- @Override
- public void onBegin(Response response)
- {
- response.abort(new Exception());
- }
- })
- .send(new Response.CompleteListener()
- {
- @Override
- public void onComplete(Result result)
- {
- Assert.assertTrue(result.isFailed());
- latch.countDown();
- }
+ Assert.assertTrue(result.isFailed());
+ latch.countDown();
});
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
}
@@ -81,23 +68,15 @@ public class HttpResponseAbortTest extends AbstractHttpClientServerTest
final CountDownLatch latch = new CountDownLatch(1);
client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
- .onResponseHeader(new Response.HeaderListener()
+ .onResponseHeader((response, field) ->
{
- @Override
- public boolean onHeader(Response response, HttpField field)
- {
- response.abort(new Exception());
- return true;
- }
+ response.abort(new Exception());
+ return true;
})
- .send(new Response.CompleteListener()
+ .send(result ->
{
- @Override
- public void onComplete(Result result)
- {
- Assert.assertTrue(result.isFailed());
- latch.countDown();
- }
+ Assert.assertTrue(result.isFailed());
+ latch.countDown();
});
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
}
@@ -110,23 +89,11 @@ public class HttpResponseAbortTest extends AbstractHttpClientServerTest
final CountDownLatch latch = new CountDownLatch(1);
client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
- .onResponseHeaders(new Response.HeadersListener()
+ .onResponseHeaders(response -> response.abort(new Exception()))
+ .send(result ->
{
- @Override
- public void onHeaders(Response response)
- {
- response.abort(new Exception());
- }
- })
- .send(new Response.CompleteListener()
- {
-
- @Override
- public void onComplete(Result result)
- {
- Assert.assertTrue(result.isFailed());
- latch.countDown();
- }
+ Assert.assertTrue(result.isFailed());
+ latch.countDown();
});
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
}
@@ -158,22 +125,11 @@ public class HttpResponseAbortTest extends AbstractHttpClientServerTest
final CountDownLatch latch = new CountDownLatch(1);
client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
- .onResponseContent(new Response.ContentListener()
+ .onResponseContent((response, content) -> response.abort(new Exception()))
+ .send(result ->
{
- @Override
- public void onContent(Response response, ByteBuffer content)
- {
- response.abort(new Exception());
- }
- })
- .send(new Response.CompleteListener()
- {
- @Override
- public void onComplete(Result result)
- {
- Assert.assertTrue(result.isFailed());
- latch.countDown();
- }
+ Assert.assertTrue(result.isFailed());
+ latch.countDown();
});
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
}
@@ -202,53 +158,31 @@ public class HttpResponseAbortTest extends AbstractHttpClientServerTest
}
});
- final CountDownLatch abortLatch = new CountDownLatch(1);
+ final DeferredContentProvider contentProvider = new DeferredContentProvider(ByteBuffer.allocate(1));
final AtomicInteger completes = new AtomicInteger();
final CountDownLatch completeLatch = new CountDownLatch(1);
client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
- .onRequestSuccess(new org.eclipse.jetty.client.api.Request.SuccessListener()
+ .content(contentProvider)
+ .onResponseContent((response, content) ->
{
- @Override
- public void onSuccess(org.eclipse.jetty.client.api.Request request)
+ try
{
- try
- {
- abortLatch.await(5, TimeUnit.SECONDS);
- }
- catch (InterruptedException x)
- {
- x.printStackTrace();
- }
+ response.abort(new Exception());
+ contentProvider.close();
+ // Delay to let the request side to finish its processing.
+ Thread.sleep(1000);
+ }
+ catch (InterruptedException x)
+ {
+ x.printStackTrace();
}
})
- .onResponseContent(new Response.ContentListener()
+ .send(result ->
{
- @Override
- public void onContent(Response response, ByteBuffer content)
- {
- try
- {
- response.abort(new Exception());
- abortLatch.countDown();
- // Delay to let the request side to finish its processing.
- Thread.sleep(1000);
- }
- catch (InterruptedException x)
- {
- x.printStackTrace();
- }
- }
- })
- .send(new Response.CompleteListener()
- {
- @Override
- public void onComplete(Result result)
- {
- completes.incrementAndGet();
- Assert.assertTrue(result.isFailed());
- completeLatch.countDown();
- }
+ completes.incrementAndGet();
+ Assert.assertTrue(result.isFailed());
+ completeLatch.countDown();
});
Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS));
diff --git a/jetty-documentation/src/main/asciidoc/administration/logging/configuring-logging-modules.adoc b/jetty-documentation/src/main/asciidoc/administration/logging/configuring-logging-modules.adoc
index 7f63f690ea1..f633dff41c3 100644
--- a/jetty-documentation/src/main/asciidoc/administration/logging/configuring-logging-modules.adoc
+++ b/jetty-documentation/src/main/asciidoc/administration/logging/configuring-logging-modules.adoc
@@ -207,7 +207,7 @@ INFO : logging-log4j initialized in ${jetty.base}/start.d/logging-log4j.ini
MKDIR : ${jetty.base}/lib/slf4j
DOWNLD: http://central.maven.org/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-api-1.7.21.jar
MKDIR : ${jetty.base}/lib/log4j
-COPY : /Users/chris/.m2/repository/log4j/log4j/1.2.17/log4j-1.2.17.jar to ${jetty.base}/lib/log4j/log4j-1.2.17.jar
+COPY : /Users/admin/.m2/repository/log4j/log4j/1.2.17/log4j-1.2.17.jar to ${jetty.base}/lib/log4j/log4j-1.2.17.jar
MKDIR : ${jetty.base}/resources
COPY : ${jetty.home}/modules/log4j-impl/resources/log4j.xml to ${jetty.base}/resources/log4j.xml
DOWNLD: http://central.maven.org/maven2/org/slf4j/slf4j-log4j12/1.7.21/slf4j-log4j12-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-log4j12-1.7.21.jar
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/chapter.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/chapter.adoc
index 6f36fb4d379..92a8cbde8d3 100644
--- a/jetty-documentation/src/main/asciidoc/administration/sessions/chapter.adoc
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/chapter.adoc
@@ -25,6 +25,8 @@ Jetty also offers more niche session managers that leverage backends such as Mon
include::session-hierarchy.adoc[]
include::sessions-details.adoc[]
+include::session-configuration-housekeeper.adoc[]
+include::session-configuration-sessioncache.adoc[]
include::session-configuration-memory.adoc[]
include::session-configuration-file-system.adoc[]
include::session-configuration-jdbc.adoc[]
@@ -32,6 +34,8 @@ include::session-configuration-mongodb.adoc[]
include::session-configuration-infinispan.adoc[]
include::session-configuration-hazelcast.adoc[]
include::session-configuration-gcloud.adoc[]
+include::session-configuration-memcachedsessiondatastore.adoc[]
+include::sessions-usecases.adoc[]
//include::setting-session-characteristics.adoc[]
//include::using-persistent-sessions.adoc[]
//include::session-clustering-jdbc.adoc[]
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-file-system.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-file-system.adoc
index db7d3cf7fed..5f2ae862691 100644
--- a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-file-system.adoc
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-file-system.adoc
@@ -16,7 +16,9 @@
[[configuring-sessions-file-system]]
-=== Non-Clustered Session Management: File System
+=== Persistent Sessions: File System
+
+Note: Persisting sessions to the local file system should *not* be used in a clustered environment.
==== Enabling File System Sessions
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-gcloud.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-gcloud.adoc
index a1b95614edd..dc4bbe37bd5 100644
--- a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-gcloud.adoc
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-gcloud.adoc
@@ -15,7 +15,8 @@
// ========================================================================
[[configuring-sessions-gcloud]]
-=== Clustered Session Management: Google Cloud DataStore
+
+=== Persistent Sessions: Google Cloud DataStore
==== Preparation
@@ -122,49 +123,49 @@ COPY : ${jetty.home}/modules/jul-impl/etc/java-util-logging.properties to ${jet
MKDIR : ${jetty.base}/lib/slf4j
DOWNLD: http://central.maven.org/maven2/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-api-1.7.21.jar
MKDIR : ${jetty.base}/lib/gcloud
-COPY : /Users/chris/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar to ${jetty.base}/lib/gcloud/aopalliance-1.0.jar
-COPY : /Users/chris/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.1.3/jackson-core-2.1.3.jar to ${jetty.base}/lib/gcloud/jackson-core-2.1.3.jar
-COPY : /Users/chris/.m2/repository/com/google/api-client/google-api-client-appengine/1.21.0/google-api-client-appengine-1.21.0.jar to ${jetty.base}/lib/gcloud/google-api-client-appengine-1.21.0.jar
-COPY : /Users/chris/.m2/repository/com/google/api-client/google-api-client/1.20.0/google-api-client-1.20.0.jar to ${jetty.base}/lib/gcloud/google-api-client-1.20.0.jar
-COPY : /Users/chris/.m2/repository/com/google/api-client/google-api-client-servlet/1.21.0/google-api-client-servlet-1.21.0.jar to ${jetty.base}/lib/gcloud/google-api-client-servlet-1.21.0.jar
+COPY : /Users/admin/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar to ${jetty.base}/lib/gcloud/aopalliance-1.0.jar
+COPY : /Users/admin/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.1.3/jackson-core-2.1.3.jar to ${jetty.base}/lib/gcloud/jackson-core-2.1.3.jar
+COPY : /Users/admin/.m2/repository/com/google/api-client/google-api-client-appengine/1.21.0/google-api-client-appengine-1.21.0.jar to ${jetty.base}/lib/gcloud/google-api-client-appengine-1.21.0.jar
+COPY : /Users/admin/.m2/repository/com/google/api-client/google-api-client/1.20.0/google-api-client-1.20.0.jar to ${jetty.base}/lib/gcloud/google-api-client-1.20.0.jar
+COPY : /Users/admin/.m2/repository/com/google/api-client/google-api-client-servlet/1.21.0/google-api-client-servlet-1.21.0.jar to ${jetty.base}/lib/gcloud/google-api-client-servlet-1.21.0.jar
DOWNLD: http://central.maven.org/maven2/com/google/api/gax/0.0.21/gax-0.0.21.jar to ${jetty.base}/lib/gcloud/gax-0.0.21.jar
-COPY : /Users/chris/.m2/repository/com/google/api/grpc/grpc-google-common-protos/0.1.0/grpc-google-common-protos-0.1.0.jar to ${jetty.base}/lib/gcloud/grpc-google-common-protos-0.1.0.jar
-COPY : /Users/chris/.m2/repository/com/google/api/grpc/grpc-google-iam-v1/0.1.0/grpc-google-iam-v1-0.1.0.jar to ${jetty.base}/lib/gcloud/grpc-google-iam-v1-0.1.0.jar
-COPY : /Users/chris/.m2/repository/com/google/auth/google-auth-library-credentials/0.3.1/google-auth-library-credentials-0.3.1.jar to ${jetty.base}/lib/gcloud/google-auth-library-credentials-0.3.1.jar
-COPY : /Users/chris/.m2/repository/com/google/auth/google-auth-library-oauth2-http/0.3.1/google-auth-library-oauth2-http-0.3.1.jar to ${jetty.base}/lib/gcloud/google-auth-library-oauth2-http-0.3.1.jar
+COPY : /Users/admin/.m2/repository/com/google/api/grpc/grpc-google-common-protos/0.1.0/grpc-google-common-protos-0.1.0.jar to ${jetty.base}/lib/gcloud/grpc-google-common-protos-0.1.0.jar
+COPY : /Users/admin/.m2/repository/com/google/api/grpc/grpc-google-iam-v1/0.1.0/grpc-google-iam-v1-0.1.0.jar to ${jetty.base}/lib/gcloud/grpc-google-iam-v1-0.1.0.jar
+COPY : /Users/admin/.m2/repository/com/google/auth/google-auth-library-credentials/0.3.1/google-auth-library-credentials-0.3.1.jar to ${jetty.base}/lib/gcloud/google-auth-library-credentials-0.3.1.jar
+COPY : /Users/admin/.m2/repository/com/google/auth/google-auth-library-oauth2-http/0.3.1/google-auth-library-oauth2-http-0.3.1.jar to ${jetty.base}/lib/gcloud/google-auth-library-oauth2-http-0.3.1.jar
DOWNLD: http://central.maven.org/maven2/com/google/auto/value/auto-value/1.2/auto-value-1.2.jar to ${jetty.base}/lib/gcloud/auto-value-1.2.jar
DOWNLD: http://central.maven.org/maven2/com/google/cloud/datastore/datastore-v1-proto-client/1.3.0/datastore-v1-proto-client-1.3.0.jar to ${jetty.base}/lib/gcloud/datastore-v1-proto-client-1.3.0.jar
DOWNLD: http://central.maven.org/maven2/com/google/cloud/datastore/datastore-v1-protos/1.3.0/datastore-v1-protos-1.3.0.jar to ${jetty.base}/lib/gcloud/datastore-v1-protos-1.3.0.jar
DOWNLD: http://central.maven.org/maven2/com/google/cloud/google-cloud-core/0.5.1/google-cloud-core-0.5.1.jar to ${jetty.base}/lib/gcloud/google-cloud-core-0.5.0.jar
DOWNLD: http://central.maven.org/maven2/com/google/cloud/google-cloud-datastore/0.5.1/google-cloud-datastore-0.5.1.jar to ${jetty.base}/lib/gcloud/google-cloud-datastore-0.5.1.jar
-COPY : /Users/chris/.m2/repository/com/google/code/findbugs/jsr305/1.3.9/jsr305-1.3.9.jar to ${jetty.base}/lib/gcloud/jsr305-1.3.9.jar
-COPY : /Users/chris/.m2/repository/com/google/code/gson/gson/2.3/gson-2.3.jar to ${jetty.base}/lib/gcloud/gson-2.3.jar
-COPY : /Users/chris/.m2/repository/com/google/guava/guava/19.0/guava-19.0.jar to ${jetty.base}/lib/gcloud/guava-19.0.jar
-COPY : /Users/chris/.m2/repository/com/google/http-client/google-http-client-appengine/1.21.0/google-http-client-appengine-1.21.0.jar to ${jetty.base}/lib/gcloud/google-http-client-appengine-1.21.0.jar
-COPY : /Users/chris/.m2/repository/com/google/http-client/google-http-client-jackson2/1.19.0/google-http-client-jackson2-1.19.0.jar to ${jetty.base}/lib/gcloud/google-http-client-jackson2-1.19.0.jar
-COPY : /Users/chris/.m2/repository/com/google/http-client/google-http-client-jackson/1.21.0/google-http-client-jackson-1.21.0.jar to ${jetty.base}/lib/gcloud/google-http-client-jackson-1.21.0.jar
-COPY : /Users/chris/.m2/repository/com/google/http-client/google-http-client/1.21.0/google-http-client-1.21.0.jar to ${jetty.base}/lib/gcloud/google-http-client-1.21.0.jar
-COPY : /Users/chris/.m2/repository/com/google/http-client/google-http-client-jdo/1.21.0/google-http-client-jdo-1.21.0.jar to ${jetty.base}/lib/gcloud/google-http-client-jdo-1.21.0.jar
-COPY : /Users/chris/.m2/repository/com/google/http-client/google-http-client-protobuf/1.20.0/google-http-client-protobuf-1.20.0.jar to ${jetty.base}/lib/gcloud/google-http-client-protobuf-1.20.0.jar
-COPY : /Users/chris/.m2/repository/com/google/inject/guice/4.0/guice-4.0.jar to ${jetty.base}/lib/gcloud/guice-4.0.jar
-COPY : /Users/chris/.m2/repository/com/google/oauth-client/google-oauth-client-appengine/1.21.0/google-oauth-client-appengine-1.21.0.jar to ${jetty.base}/lib/gcloud/google-oauth-client-appengine-1.21.0.jar
-COPY : /Users/chris/.m2/repository/com/google/oauth-client/google-oauth-client/1.21.0/google-oauth-client-1.21.0.jar to ${jetty.base}/lib/gcloud/google-oauth-client-1.21.0.jar
-COPY : /Users/chris/.m2/repository/com/google/oauth-client/google-oauth-client-servlet/1.21.0/google-oauth-client-servlet-1.21.0.jar to ${jetty.base}/lib/gcloud/google-oauth-client-servlet-1.21.0.jar
-COPY : /Users/chris/.m2/repository/com/google/protobuf/protobuf-java/3.0.0/protobuf-java-3.0.0.jar to ${jetty.base}/lib/gcloud/protobuf-java-3.0.0.jar
-COPY : /Users/chris/.m2/repository/com/google/protobuf/protobuf-java-util/3.0.0/protobuf-java-util-3.0.0.jar to ${jetty.base}/lib/gcloud/protobuf-java-util-3.0.0.jar
-COPY : /Users/chris/.m2/repository/commons-codec/commons-codec/1.3/commons-codec-1.3.jar to ${jetty.base}/lib/gcloud/commons-codec-1.3.jar
-COPY : /Users/chris/.m2/repository/io/grpc/grpc-context/1.0.1/grpc-context-1.0.1.jar to ${jetty.base}/lib/gcloud/grpc-context-1.0.1.jar
-COPY : /Users/chris/.m2/repository/io/grpc/grpc-core/1.0.1/grpc-core-1.0.1.jar to ${jetty.base}/lib/gcloud/grpc-core-1.0.1.jar
-COPY : /Users/chris/.m2/repository/io/grpc/grpc-protobuf/1.0.1/grpc-protobuf-1.0.1.jar to ${jetty.base}/lib/gcloud/grpc-protobuf-1.0.1.jar
-COPY : /Users/chris/.m2/repository/io/grpc/grpc-protobuf-lite/1.0.1/grpc-protobuf-lite-1.0.1.jar to ${jetty.base}/lib/gcloud/grpc-protobuf-lite-1.0.1.jar
-COPY : /Users/chris/.m2/repository/javax/inject/javax.inject/1/javax.inject-1.jar to ${jetty.base}/lib/gcloud/javax.inject-1.jar
-COPY : /Users/chris/.m2/repository/javax/jdo/jdo2-api/2.3-eb/jdo2-api-2.3-eb.jar to ${jetty.base}/lib/gcloud/jdo2-api-2.3-eb.jar
-COPY : /Users/chris/.m2/repository/javax/transaction/transaction-api/1.1/transaction-api-1.1.jar to ${jetty.base}/lib/gcloud/transaction-api-1.1.jar
-COPY : /Users/chris/.m2/repository/joda-time/joda-time/2.9.2/joda-time-2.9.2.jar to ${jetty.base}/lib/gcloud/joda-time-2.9.2.jar
-COPY : /Users/chris/.m2/repository/org/apache/httpcomponents/httpclient/4.0.1/httpclient-4.0.1.jar to ${jetty.base}/lib/gcloud/httpclient-4.0.1.jar
-COPY : /Users/chris/.m2/repository/org/apache/httpcomponents/httpcore/4.0.1/httpcore-4.0.1.jar to ${jetty.base}/lib/gcloud/httpcore-4.0.1.jar
-COPY : /Users/chris/.m2/repository/org/codehaus/jackson/jackson-core-asl/1.9.11/jackson-core-asl-1.9.11.jar to ${jetty.base}/lib/gcloud/jackson-core-asl-1.9.11.jar
-COPY : /Users/chris/.m2/repository/org/json/json/20151123/json-20151123.jar to ${jetty.base}/lib/gcloud/json-20151123.jar
+COPY : /Users/admin/.m2/repository/com/google/code/findbugs/jsr305/1.3.9/jsr305-1.3.9.jar to ${jetty.base}/lib/gcloud/jsr305-1.3.9.jar
+COPY : /Users/admin/.m2/repository/com/google/code/gson/gson/2.3/gson-2.3.jar to ${jetty.base}/lib/gcloud/gson-2.3.jar
+COPY : /Users/admin/.m2/repository/com/google/guava/guava/19.0/guava-19.0.jar to ${jetty.base}/lib/gcloud/guava-19.0.jar
+COPY : /Users/admin/.m2/repository/com/google/http-client/google-http-client-appengine/1.21.0/google-http-client-appengine-1.21.0.jar to ${jetty.base}/lib/gcloud/google-http-client-appengine-1.21.0.jar
+COPY : /Users/admin/.m2/repository/com/google/http-client/google-http-client-jackson2/1.19.0/google-http-client-jackson2-1.19.0.jar to ${jetty.base}/lib/gcloud/google-http-client-jackson2-1.19.0.jar
+COPY : /Users/admin/.m2/repository/com/google/http-client/google-http-client-jackson/1.21.0/google-http-client-jackson-1.21.0.jar to ${jetty.base}/lib/gcloud/google-http-client-jackson-1.21.0.jar
+COPY : /Users/admin/.m2/repository/com/google/http-client/google-http-client/1.21.0/google-http-client-1.21.0.jar to ${jetty.base}/lib/gcloud/google-http-client-1.21.0.jar
+COPY : /Users/admin/.m2/repository/com/google/http-client/google-http-client-jdo/1.21.0/google-http-client-jdo-1.21.0.jar to ${jetty.base}/lib/gcloud/google-http-client-jdo-1.21.0.jar
+COPY : /Users/admin/.m2/repository/com/google/http-client/google-http-client-protobuf/1.20.0/google-http-client-protobuf-1.20.0.jar to ${jetty.base}/lib/gcloud/google-http-client-protobuf-1.20.0.jar
+COPY : /Users/admin/.m2/repository/com/google/inject/guice/4.0/guice-4.0.jar to ${jetty.base}/lib/gcloud/guice-4.0.jar
+COPY : /Users/admin/.m2/repository/com/google/oauth-client/google-oauth-client-appengine/1.21.0/google-oauth-client-appengine-1.21.0.jar to ${jetty.base}/lib/gcloud/google-oauth-client-appengine-1.21.0.jar
+COPY : /Users/admin/.m2/repository/com/google/oauth-client/google-oauth-client/1.21.0/google-oauth-client-1.21.0.jar to ${jetty.base}/lib/gcloud/google-oauth-client-1.21.0.jar
+COPY : /Users/admin/.m2/repository/com/google/oauth-client/google-oauth-client-servlet/1.21.0/google-oauth-client-servlet-1.21.0.jar to ${jetty.base}/lib/gcloud/google-oauth-client-servlet-1.21.0.jar
+COPY : /Users/admin/.m2/repository/com/google/protobuf/protobuf-java/3.0.0/protobuf-java-3.0.0.jar to ${jetty.base}/lib/gcloud/protobuf-java-3.0.0.jar
+COPY : /Users/admin/.m2/repository/com/google/protobuf/protobuf-java-util/3.0.0/protobuf-java-util-3.0.0.jar to ${jetty.base}/lib/gcloud/protobuf-java-util-3.0.0.jar
+COPY : /Users/admin/.m2/repository/commons-codec/commons-codec/1.3/commons-codec-1.3.jar to ${jetty.base}/lib/gcloud/commons-codec-1.3.jar
+COPY : /Users/admin/.m2/repository/io/grpc/grpc-context/1.0.1/grpc-context-1.0.1.jar to ${jetty.base}/lib/gcloud/grpc-context-1.0.1.jar
+COPY : /Users/admin/.m2/repository/io/grpc/grpc-core/1.0.1/grpc-core-1.0.1.jar to ${jetty.base}/lib/gcloud/grpc-core-1.0.1.jar
+COPY : /Users/admin/.m2/repository/io/grpc/grpc-protobuf/1.0.1/grpc-protobuf-1.0.1.jar to ${jetty.base}/lib/gcloud/grpc-protobuf-1.0.1.jar
+COPY : /Users/admin/.m2/repository/io/grpc/grpc-protobuf-lite/1.0.1/grpc-protobuf-lite-1.0.1.jar to ${jetty.base}/lib/gcloud/grpc-protobuf-lite-1.0.1.jar
+COPY : /Users/admin/.m2/repository/javax/inject/javax.inject/1/javax.inject-1.jar to ${jetty.base}/lib/gcloud/javax.inject-1.jar
+COPY : /Users/admin/.m2/repository/javax/jdo/jdo2-api/2.3-eb/jdo2-api-2.3-eb.jar to ${jetty.base}/lib/gcloud/jdo2-api-2.3-eb.jar
+COPY : /Users/admin/.m2/repository/javax/transaction/transaction-api/1.1/transaction-api-1.1.jar to ${jetty.base}/lib/gcloud/transaction-api-1.1.jar
+COPY : /Users/admin/.m2/repository/joda-time/joda-time/2.9.2/joda-time-2.9.2.jar to ${jetty.base}/lib/gcloud/joda-time-2.9.2.jar
+COPY : /Users/admin/.m2/repository/org/apache/httpcomponents/httpclient/4.0.1/httpclient-4.0.1.jar to ${jetty.base}/lib/gcloud/httpclient-4.0.1.jar
+COPY : /Users/admin/.m2/repository/org/apache/httpcomponents/httpcore/4.0.1/httpcore-4.0.1.jar to ${jetty.base}/lib/gcloud/httpcore-4.0.1.jar
+COPY : /Users/admin/.m2/repository/org/codehaus/jackson/jackson-core-asl/1.9.11/jackson-core-asl-1.9.11.jar to ${jetty.base}/lib/gcloud/jackson-core-asl-1.9.11.jar
+COPY : /Users/admin/.m2/repository/org/json/json/20151123/json-20151123.jar to ${jetty.base}/lib/gcloud/json-20151123.jar
DOWNLD: http://central.maven.org/maven2/org/slf4j/jcl-over-slf4j/1.7.21/jcl-over-slf4j-1.7.21.jar to ${jetty.base}/lib/slf4j/jcl-over-slf4j-1.7.21.jar
COPY : ${jetty.home}/modules/gcloud/index.yaml to ${jetty.base}/etc/index.yaml
INFO : Base directory was modified
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-hazelcast.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-hazelcast.adoc
index a424404e149..4f79c24aac8 100644
--- a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-hazelcast.adoc
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-hazelcast.adoc
@@ -16,7 +16,7 @@
[[configuring-sessions-hazelcast]]
-=== Clustered Session Management: Hazelcast
+=== Persistent Sessions: Hazelcast
==== Enabling Hazelcast Sessions
@@ -24,10 +24,11 @@ When using the Jetty distribution, you will first need to enable the `session-st
[source, screen, subs="{sub-order}"]
----
-mb-olamy:tmp-base olamy$ java -jar ../start.jar --create-startd
+$ java -jar ../start.jar --create-startd
MKDIR : ${jetty.base}/start.d
INFO : Base directory was modified
-mb-olamy:tmp-base olamy$ java -jar ../start.jar --add-to-start=session-store-hazelcast-remote
+
+$ java -jar ../start.jar --add-to-start=session-store-hazelcast-remote
ALERT: There are enabled module(s) with licenses.
The following 1 module(s):
@@ -44,11 +45,11 @@ Proceed (y/N)? y
INFO : server transitively enabled, ini template available with --add-to-start=server
INFO : sessions transitively enabled, ini template available with --add-to-start=sessions
INFO : session-store-hazelcast-remote initialized in ${jetty.base}/start.d/session-store-hazelcast-remote.ini
-MKDIR : /Users/olamy/mvn-repo/com/hazelcast/hazelcast/3.8.2
-DOWNLD: http://central.maven.org/maven2/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar to /Users/olamy/mvn-repo/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar
+MKDIR : /Users/admin/mvn-repo/com/hazelcast/hazelcast/3.8.2
+DOWNLD: http://central.maven.org/maven2/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar to /Users/admin/mvn-repo/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar
MKDIR : ${jetty.base}/lib/hazelcast
-COPY : /Users/olamy/mvn-repo/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar to ${jetty.base}/lib/hazelcast/hazelcast-3.8.2.jar
-COPY : /Users/olamy/mvn-repo/com/hazelcast/hazelcast-client/3.8.2/hazelcast-client-3.8.2.jar to ${jetty.base}/lib/hazelcast/hazelcast-client-3.8.2.jar
+COPY : /Users/admin/mvn-repo/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar to ${jetty.base}/lib/hazelcast/hazelcast-3.8.2.jar
+COPY : /Users/admin/mvn-repo/com/hazelcast/hazelcast-client/3.8.2/hazelcast-client-3.8.2.jar to ${jetty.base}/lib/hazelcast/hazelcast-client-3.8.2.jar
INFO : Base directory was modified
----
@@ -112,10 +113,10 @@ To enable this you will first need to enable the `session-store-hazelcast-embedd
[source, screen, subs="{sub-order}"]
----
-mb-olamy:tmp-base olamy$ java -jar ../start.jar --create-startd
+$ java -jar ../start.jar --create-startd
MKDIR : ${jetty.base}/start.d
INFO : Base directory was modified
-mb-olamy:tmp-base olamy$ java -jar ../start.jar --add-to-start=session-store-hazelcast-embedded
+$ java -jar ../start.jar --add-to-start=session-store-hazelcast-embedded
ALERT: There are enabled module(s) with licenses.
The following 1 module(s):
@@ -132,11 +133,11 @@ Proceed (y/N)? y
INFO : server transitively enabled, ini template available with --add-to-start=server
INFO : sessions transitively enabled, ini template available with --add-to-start=sessions
INFO : session-store-hazelcast-embedded initialized in ${jetty.base}/start.d/session-store-hazelcast-embedded.ini
-MKDIR : /Users/olamy/mvn-repo/com/hazelcast/hazelcast/3.8.2
-DOWNLD: http://central.maven.org/maven2/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar to /Users/olamy/mvn-repo/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar
+MKDIR : /Users/admin/mvn-repo/com/hazelcast/hazelcast/3.8.2
+DOWNLD: http://central.maven.org/maven2/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar to /Users/admin/mvn-repo/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar
MKDIR : ${jetty.base}/lib/hazelcast
-COPY : /Users/olamy/mvn-repo/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar to ${jetty.base}/lib/hazelcast/hazelcast-3.8.2.jar
-COPY : /Users/olamy/mvn-repo/com/hazelcast/hazelcast-client/3.8.2/hazelcast-client-3.8.2.jar to ${jetty.base}/lib/hazelcast/hazelcast-client-3.8.2.jar
+COPY : /Users/admin/mvn-repo/com/hazelcast/hazelcast/3.8.2/hazelcast-3.8.2.jar to ${jetty.base}/lib/hazelcast/hazelcast-3.8.2.jar
+COPY : /Users/admin/mvn-repo/com/hazelcast/hazelcast-client/3.8.2/hazelcast-client-3.8.2.jar to ${jetty.base}/lib/hazelcast/hazelcast-client-3.8.2.jar
----
Doing this enables the embedded Hazelcast Session module and any dependent modules or files needed for it to run on the server.
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-housekeeper.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-housekeeper.adoc
new file mode 100644
index 00000000000..587cf65cc2f
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-housekeeper.adoc
@@ -0,0 +1,54 @@
+// ========================================================================
+// 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.
+// ========================================================================
+
+[[session-configuration-housekeeper]]
+=== The SessionIdManager and the Housekeeper
+
+==== Default Settings
+By default, Jetty will instantiate a single instance of the `DefaultSessionIdManager` and `HouseKeeper` at startup with default settings.
+
+The default settings are:
+
+DefaultSessionIdManager: worker name::
+This uniquely identifies the jetty server instance within a cluster.
+It is set from the value of the `JETTY_WORKER_INSTANCE` environment variable, or `node0` if the environment value is not set.
+If you have more than one Jetty instance, it is *crucial* that you explicitly configure the worker name on each Jetty instance (see link:#session-idmanager-housekeeper-config[below] for how to configure).
+
+HouseKeeper: scavenge interval::
+This is the period in seconds between runs of the session scavenger, and by default is set to the equivalent of 10 minutes.
+As a rule of thumb, you should ensure that the scavenge interval is shorter than the `maxInactiveInterval` of your sessions to ensure that they are promptly scavenged.
+See below for instructions on how to configure this.
+
+[[session-idmanager-housekeeper-config]]
+==== Configuration
+To change the default values, use the link:#startup-modules[module system] to link:#startup-modules[enable] the `sessions` module.
+
+This will enable the `$jetty.home/etc/sessions/id-manager.xml` file and generate a `$jetty.base/start.d/sessions.ini` file.
+
+The `id-manager.xml` file instantiates a single `DefaultSessionIdManager` and `HouseKeeper` and configures them using the properties from the `sessions.ini` file.
+
+Edit the ini file to change the properties to easily customize the `DefaultSessionIdManager` and `HouseKeeper`:
+
+jetty.sessionIdManager.workerName::
+By default it is `node1`.
+This uniquely identifies the Jetty server instance within a cluster.
+If you have more than one Jetty instance, it is crucial that you configure the worker name differently on each jetty instance.
+
+
+jetty.sessionScavengeInterval.seconds::
+This is the period in seconds between runs of the session scavenger.
+By default it will run every 600 secs (ie 10 mins).
+As a rule of thumb, you should ensure that the scavenge interval is shorter than the maxInactiveInterval of your sessions to ensure that they are promptly scavenged.
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-infinispan.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-infinispan.adoc
index c4cad1f7669..8dd7827eed6 100644
--- a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-infinispan.adoc
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-infinispan.adoc
@@ -16,12 +16,17 @@
[[configuring-sessions-infinispan]]
-=== Clustered Session Management: Inifinspan
+=== Persistent Sessions: Inifinspan
==== Enabling Infinispan Sessions
When using the Jetty distribution, you will first need to enable the `session-store-infinispan-remote` link:#startup-modules[module] for your link:#startup-base-and-home[Jetty base] using the `--add-to-start` argument on the command line.
+____
+[IMPORTANT]
+If you are running Jetty with JDK 9 or greater, enable `session-store-infinispan-remote-910.mod` instead.
+____
+
[source, screen, subs="{sub-order}"]
----
$ java -jar ../start.jar --create-startd
@@ -106,6 +111,11 @@ ____
During testing, it can be helpful to run an in-process instance of Infinispan.
To enable this you will first need to enable the `session-store-infinispan-embedded` link:#startup-modules[module] for your link:#startup-base-and-home[Jetty base] using the `--add-to-start` argument on the command line.
+____
+[IMPORTANT]
+If you are running Jetty with JDK 9 or greater, enable `session-store-infinispan-embedded-910.mod` instead.
+____
+
[source, screen, subs="{sub-order}"]
----
java -jar ../start.jar --add-to-start=session-store-infinispan-embedded
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-jdbc.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-jdbc.adoc
index 4902b880d33..4d095ba88dc 100644
--- a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-jdbc.adoc
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-jdbc.adoc
@@ -16,7 +16,7 @@
[[configuring-sessions-jdbc]]
-=== Clustered Session Management: JDBC
+=== Persistent Sessions: JDBC
==== Enabling JDBC Sessions
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-memcachedsessiondatastore.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-memcachedsessiondatastore.adoc
new file mode 100644
index 00000000000..9ccfd52553a
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-memcachedsessiondatastore.adoc
@@ -0,0 +1,62 @@
+// ========================================================================
+// 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.
+// ========================================================================
+
+[[session-configuration-memcachedsessiondatastore]]
+
+=== Persistent Sessions: The L2 Session Data Cache
+
+If your chosen persistence technology is slow, it can be helpful to locally cache the session data.
+The `CachingSessionDataStore` is a special type of `SessionDataStore` that locally caches session data, which makes reads faster. It writes-through to your chosen type of `SessionDataStore` when session data changes.
+
+==== MemcachedSessionDataMap
+
+The `MemcachedSessionDataMap` uses `memcached` to perform caching.
+
+To enable it with the Jetty distribution, enable the `session-store-cache` link:#startup-modules[module], along with your chosen `session-store-xxxx` module, and optionally the `session-cache-hash` or `session-cache-null` modules.
+
+After enabling, the `$jetty.base/start.d/session-store-cache.ini` file will be generated:
+
+[source, screen, subs="{sub-order}"]
+----
+--module=session-store-cache
+
+
+## Session Data Cache type: xmemcached
+session-data-cache=xmemcached
+#jetty.session.memcached.host=localhost
+#jetty.session.memcached.port=11211
+#jetty.session.memcached.expirySec=
+#jetty.session.memcached.heartbeats=true
+----
+
+
+The configuration properties are:
+
+jetty.session.memcached.host::
+Default value is `localhost`.
+This is the host on which the memcached server resides.
+
+jetty.session.memcached.port::
+Default value is `11211`.
+This is the port on which the memcached server is listening.
+
+jetty.session.memcached.expirySec::
+Default value `0`.
+This is the length of time in seconds that an item can remain in the memcached cache, where 0 indicates indefinitely.
+
+jetty.session.memcached.heartbeats::
+Default value `true`.
+Whether or not the memcached system should generate heartbeats.
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-memory.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-memory.adoc
index 3f72e4bdac9..3fb65192ec8 100644
--- a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-memory.adoc
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-memory.adoc
@@ -16,21 +16,16 @@
[[configuring-sessions-memory]]
-=== Non-Clustered Session Management: Memory
+=== Non-Persistent Sessions
-Non-clustered, in-memory-only is the default style of Session Management.
-In previous versions of Jetty this was referred to as "hash" sessions, as they were stored in a `HashMap` in memory.
-When using the Jetty distribution, if you do not configure any session module, this will be enabled by default.
+Non-clustered, non-persistent, in-memory-only is the default style of session management.
+In previous versions of Jetty this was referred to as "hash" sessions, as they were stored in a `HashMap` in memory.
-Specifically, Jetty will hook up:
+This is delivered by a combination of the `DefaultSessionCache` (to keep sessions in memory) and a `NullSessionDataStore` (to avoid session persistence).
-A `DefaultSessionIdManager`::
-* Produces unique session ids and supports cross-context dispatch re-use of session ids
-A `HouseKeeper`::
-* Scavenges for expired sessions every 10 mins
-A `DefaultSessionCache` per context::
-* Keeps session objects in memory
-A `NullSessionDataStore` per context::
-* No persistence of sessions
+If you do nothing, Jetty will instantiate one of each of these objects for each context at startup time using hard-coded defaults.
-If you wish to change any of the default configuration, enable the `session-cache-hash` module.
+To explicitly set up non-persisted sessions using modules, use both the `session-cache-hash` and the `session-store-null` modules.
+
+Enabling the modules allows you to configure behavior - see link:#session-configuration-sessioncache[the L1 Session Cache] for detailed information on configuration options for the `DefaultSessionCache`.
+The `NullSessionDataStore` has no customizable options.
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-mongodb.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-mongodb.adoc
index 770b90456b1..c038acb96e7 100644
--- a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-mongodb.adoc
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-mongodb.adoc
@@ -16,7 +16,7 @@
[[configuring-sessions-mongo]]
-=== Clustered Session Management: MongoDB
+=== Persistent Sessions: MongoDB
==== Enabling MongoDB Sessions
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-sessioncache.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-sessioncache.adoc
new file mode 100644
index 00000000000..69b59acb044
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/session-configuration-sessioncache.adoc
@@ -0,0 +1,73 @@
+// ========================================================================
+// 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.
+// ========================================================================
+
+[[session-configuration-sessioncache]]
+=== The L1 Session Cache
+
+==== The DefaultSessionCache
+
+In the absence of any explicit configuration, Jetty will instantiate an instance of the `DefaultSessionCache` per context.
+If you wish to change any of the default values, you need to enable the `session-cache-hash` link:#startup-modules[module].
+
+Once the `session-cache-hash` module has been enabled, you can view a list of all the configurable values by opening `start.d/session-cache-hash.ini`:
+
+[source, screen, subs="{sub-order}"]
+----
+--module=session-cache-hash
+
+#jetty.session.evictionPolicy=-1
+#jetty.session.saveOnInactiveEvict=false
+#jetty.session.saveOnCreate=false
+#jetty.session.removeUnloadableSessions=false
+----
+
+jetty.session.evictionPolicy::
+Integer.
+Controls whether session objects that are held in memory are subject to eviction from the memory cache.
+Evicting sessions can reduce the memory footprint of the cache.
+Eviction is usually used in conjunction with a `SessionDataStore` that persists sessions.
+Values are:
+* -1 : sessions are never evicted from the cache
+* 0 : sessions are evicted from the cache as soon as the last active request for it finishes
+* >= 1 : any positive number is the time in seconds after which a session that is in the cache but has not experienced any activity will be evicted
+
+____
+[NOTE]
+If you are not using a `SessionDataStore` that persists sessions, be aware that evicted sessions will be lost.
+____
+
+jetty.session.saveOnInactiveEvict::
+Boolean, default `false`.
+Controls whether a session will be saved to the `SessionDataStore` just prior to its eviction.
+
+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`.
+
+For more general information on the uses of these configuration properties, see link:#sessions-details[Session Components].
+
+
+==== The NullSessionCache
+
+The `NullSessionCache` is a trivial implementation of the `SessionCache` that does not cache any session information.
+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].
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/session-hierarchy.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/session-hierarchy.adoc
index 7552dc01864..d5539736b82 100644
--- a/jetty-documentation/src/main/asciidoc/administration/sessions/session-hierarchy.adoc
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/session-hierarchy.adoc
@@ -49,8 +49,11 @@ image::images/SessionsHierarchy.png[]
==== Configuring Sessions in the Jetty Distribution
-Jetty provides support for several different Session Management technologies.
-Both link:#configuring-sessions-file-system[local file storage] and in-memory session management can be implemented for standard implementations.
-For implementations using clustered technologies, link:#configuring-sessions-jdbc[JDBC], link:#configuring-sessions-mongo[MongoDB], link:#configuring-sessions-infinispan[Inifinispan] and link:#configuring-sessions-gcloud[Google Cloud Datastore] are all supported.
-Setting up these technologies is as easy as enabling it's link:#startup-modules[module] and editing it's associated ini file with any usernames, passwords or changes you need to make for your instance.
-The following sections will cover how exactly to enable the required modules as well as an overview of what options are available for customization.
+Configuring session management involves selecting a link:#startup-modules[module] for the desired type of link:#session-configuration-sessioncache[session caching] behavior, and a module for the type of session persistence.
+
+Jetty provides two different session caches: the `DefaultSessionCache` which holds sessions in memory, and the `NullSessionCache` which does not.
+There is more information on both of these types of session caching and the circumstances which would lead you to select one or the other in the link:#sessions-details[Session Components] section, and more information on the configuration options of each in link:#session-configuration-sessioncache[the L1 Session Cache] section.
+
+For session persistence, Jetty provides a number of different implementations from which to choose including link:#configuring-sessions-memory[non-persistence], link:#configuring-sessions-file-system[local file storage], clustered technologies such as link:#configuring-sessions-jdbc[JDBC], link:#configuring-sessions-mongo[MongoDB], link:#configuring-sessions-infinispan[Inifinispan], link:#configuring-sessions-gcloud[Google Cloud Datastore], and link:#configuring-sessions-hazelcast[Hazelcast].
+
+Depending on your persistence technology, to enhance performance, you may want to use an L2 cache for session data, in which case Jetty provides the link:#session-configuration-memcachedsessiondatastore[memcached L2 session data cache].
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/sessions-details.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/sessions-details.adoc
index 20cb4a3eb5f..dac104a6f26 100644
--- a/jetty-documentation/src/main/asciidoc/administration/sessions/sessions-details.adoc
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/sessions-details.adoc
@@ -15,40 +15,43 @@
// ========================================================================
[[sessions-details]]
-=== Session Configuration and Use Cases
+=== Session Components
-==== Configuration
+==== SessionIdManager
-===== SessionIdManager
-
-There is a maximum of 1 `SessionIdManager` per jetty Server instance.
+There is a maximum of one (1) `SessionIdManager` per Jetty Server instance.
Its purpose is to generate fresh, unique session ids and to coordinate the re-use of session ids amongst co-operating contexts.
Unlike in previous versions of Jetty, the `SessionIdManager` is agnostic with respect to the type of clustering technology chosen.
Jetty provides a default implementation - the `DefaultSessionIdManager` - which should meet the needs of most users.
-If you do not explicitly enable one of the session modules, or otherwise configure a `SessionIdManager`, the `DefaultSessionIdManager` will be used.
+If you do not explicitly enable one of the session modules or otherwise configure a `SessionIdManager`, the `DefaultSessionIdManager` will be used.
If the `DefaultSessionIdManager` does not meet your needs, you can extend the `org.eclipse.jetty.server.session.AbstractSessionIdManager` or do a fresh implementation of the `org.eclipse.jetty.server.session.SessionIdManager` interface.
-===== HouseKeeper
+See link:#session-configuration-housekeeper[Configuring the SessionIdManager and HouseKeeper] for details on configuration.
-There is a maximum of 1 `HouseKeeper` per `SessionIdManager`.
+==== HouseKeeper
+
+There is a maximum of one (1) `HouseKeeper` per `SessionIdManager`.
Its purpose is to periodically poll the `SessionHandlers` to clean out expired sessions.
By default the `HouseKeeper` will poll the `SessionHandlers` every 10 mins to find and delete expired sessions, although this interval is configurable.
+See link:#session-configuration-housekeeper[Configuring the SessionIdManager and HouseKeeper] for details on configuration.
-===== SessionCache
-There is 1 `SessionCache` per context.
+==== SessionCache
+
+There is one (1) `SessionCache` *per context.*
Its purpose is to provide an L1 cache of Session objects.
Having a working set of Session objects in memory allows multiple simultaneous requests for the same session to share the same Session object.
-Jetty provides 2 `SessionCache` implementations: the `DefaultSessionCache` and the `NullSessionCache`.
-The `DefaultSessionCache` retains Session objects in memory in a cache and has a number of configuration options to control cache behavior.
+Jetty provides two (2) `SessionCache` implementations: the `DefaultSessionCache` and the `NullSessionCache`.
+The `DefaultSessionCache` retains Session objects in memory in a cache and has a number of link:#session-configuration-sessioncache[configuration options] to control cache behavior.
It is the default that is used if no other `SessionCache` has been configured.
It is suitable for non-clustered and clustered deployments with a sticky load balancer, as well as clustered deployments with a non-sticky load balancer, with some caveats.
+
The `NullSessionCache` does not actually cache any objects: each request uses a fresh Session object.
It is suitable for clustered deployments without a sticky load balancer and non-clustered deployments when purely minimal support for sessions is needed.
@@ -58,16 +61,19 @@ They can also be configured to do an immediate, eager write of a freshly created
This can be useful if you are likely to experience multiple, near simultaneous requests referencing the same session, e.g. with HTTP/2 and you don't have a sticky load balancer.
Alternatively, if the eager write is not done, application paths which create and then invalidate a session within a single request never incur the cost of writing to persistent storage.
-Additionally, if the `EVICT_ON_INACTIVITY` eviction policy is in use, you can configure the `DefaultSessionCache` to force a write of the Session to the SessionDataStore just before the Session is evicted.
+Additionally, if the `EVICT_ON_INACTIVITY` eviction policy is in use, you can link:#session-configuration-sessioncache[configure] the `DefaultSessionCache` to force a write of the Session to the `SessionDataStore` just before the Session is evicted.
-===== SessionDataStore
+See link:#session-configuration-sessioncache[the L1 Session Cache] for more information.
-There is 1 `SessionDataStore` per context. Its purpose is to handle all persistence related operations on sessions.
+==== SessionDataStore
+
+There is one (1) `SessionDataStore` per context.
+Its purpose is to handle all persistence related operations on sessions.
The common characteristics for all `SessionDataStores` are whether or not they support passivation, and the length of the grace period.
Supporting passivation means that session data is serialized.
-Some persistence mechanisms serialize, such as JDBC, GCloud Datastore etc, whereas others may store an object in shared memory eg Infinispan when configured with a local cache.
+Some persistence mechanisms serialize, such as JDBC, GCloud Datastore etc, whereas others may store an object in shared memory, e.g. Infinispan, when configured with a local cache.
Whether or not a clustering technology entails passivation controls whether or not the session passivation/activation listeners will be called.
@@ -79,56 +85,17 @@ When `SessionDataStores` search their persistent store to find sessions that hav
* The second finds sessions in the store that have expired which were last live on the current node
* The third finds sessions that expired a "while" ago, irrespective of on which node they were last used: the definition of "a while" is based on the grace period.
+Jetty instantiates the trivial `NullSessionDataStore` - which does not persist sessions - as the default.
-===== CachingSessionDataStore
+The distribution provides a number of alternative `SessionDataStore` implementations such as link:#configuring-sessions-file-system[FileSessionDataStore], link:#configuring-sessions-gcloud[GCloudSessionDataStore], link:#configuring-sessions-jdbc[JDBCSessionDataStore], link:#configuring-sessions-mongodb[MongoSessionDataStore], link:#configuring-sessions-infinispan[InfinispanSessionDataStore], link:#configuring-sessions-hazelcast[HazelcastSessionDataStore].
-The `CachingSessionDataStore` is a special type of `SessionDataStore` that inserts an L2 cache of SessionData - the `SessionDataMap` - in front of a delegate `SessionDataStore`.
+
+==== CachingSessionDataStore
+
+The `CachingSessionDataStore` is a special type of `SessionDataStore` that inserts an L2 cache of Session data - the `SessionDataMap` - in front of a delegate `SessionDataStore`.
The `SessionDataMap` is preferentially consulted before the actual SessionDataStore on reads.
This can improve the performance of slow stores.
Jetty provides one implementation of the this L2 cache based on `Memcached`, the `MemcachedSessionDataMap`.
-
-==== Use Cases
-
-===== Clustering with a Sticky Load Balancer
-
-Preferably, your cluster will utilize a sticky load balancer.
-This will route requests for the same session to the same Jetty instance.
-In this case, the `DefaultSessionCache` can be used to keep in-use Session objects in memory.
-You can fine-tune the cache by controlling how long Session objects remain in memory with the eviction policy settings.
-
-If you have a large number of Sessions or very large Session objects, then you might want to manage your memory allocation by controlling the amount of time Session objects spend in the cache.
-The `EVICT_ON_SESSION_EXIT` eviction policy will remove a Session object from the cache as soon as the last simultaneous request referencing it exits.
-Alternatively, the `EVICT_ON_INACTIVITY` policy will remove a Session object from the cache after a configurable amount of time has passed without a request referencing it.
-
-If your Sessions are very long lived and infrequently referenced, you might use the `EVICT_ON_INACTIVITY_POLICY` to control the size of the cache.
-
-If your Sessions are small, or relatively few or stable in number or they are read-mostly, then you might select the `NEVER_EVICT` policy.
-With this policy, Session objects will remain in the cache until they either expire or are explicitly invalidated.
-
-If you have a high likelihood of simultaneous requests for the same session object, then the `EVICT_ON_SESSION_EXIT` policy will ensure the Session object stays in the cache as long as it is needed.
-
-
-===== Clustering without a Sticky Load Balancer
-
-Without a sticky load balancer requests for the same session may arrive on any node in the cluster.
-This means it is likely that the copy of the Session object in any `SessionCache` is likely to be out-of-date, as the Session was probably last accessed on a different node.
-In this case, your `choices` are to use either the `NullSessionCache` or to de-tuned the `DefaultSessionCache`.
-If you use the NullSessionCache all Session object caching is avoided.
-This means that every time a request references a session it must be brought in from persistent storage.
-It also means that there can be no sharing of Session objects for multiple requests for the same session: each will have their own Session object.
-Furthermore, the outcome of session writes are indeterminate because the Servlet Specification does not mandate ACID transactions for sessions.
-
-If you use the `DefaultSessionCache`, there is a risk that the caches on some nodes will contain out-of-date session information as simultaneous requests for the same session are scattered over the cluster.
-To mitigate this somewhat you can use the `EVICT_ON_SESSION_EXIT` eviction policy: this will ensure that the Session is removed from the cache as soon as the last simultaneous request for it exits.
-Again, due to the lack of session transactionality, the ordering outcome of write operations cannot be guaranteed.
-As the Session is cached while at least one request is accessing it, it is possible for multiple simultaneous requests to share the same Session object.
-
-
-===== Handling corrupted or unloadable session data
-
-For various reasons it might not be possible for the SessionDataStore to re-read a stored session.
-One scenario is that the session stores a serialized object in it's attributes, and after a redeployment there in an incompatible class change.
-Using the setter `SessionCache.setRemoveUnloadableSessions(true)` will allow the `SessionDataStore` to delete the unreadable session from persistent storage.
-This can be useful from preventing the scavenger from continually generating errors on the same expired, but un-restorable, session.
+See link:#session-configuration-memcachedsessiondatastore[the L2 SessionData Cache]for additional information.
diff --git a/jetty-documentation/src/main/asciidoc/administration/sessions/sessions-usecases.adoc b/jetty-documentation/src/main/asciidoc/administration/sessions/sessions-usecases.adoc
new file mode 100644
index 00000000000..57600be6ad5
--- /dev/null
+++ b/jetty-documentation/src/main/asciidoc/administration/sessions/sessions-usecases.adoc
@@ -0,0 +1,60 @@
+// ========================================================================
+// 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.
+// ========================================================================
+
+[[sessions-usecases]]
+=== Use Cases
+
+==== Clustering with a Sticky Load Balancer
+
+Preferably, your cluster will utilize a sticky load balancer.
+This will route requests for the same Session to the same Jetty instance.
+In this case, the `DefaultSessionCache` can be used to keep in-use Session objects in memory.
+You can fine-tune the cache by controlling how long Session objects remain in memory with the eviction policy settings.
+
+If you have a large number of Sessions or very large Session objects, then you may want to manage your memory allocation by controlling the amount of time Session objects spend in the cache.
+The `EVICT_ON_SESSION_EXIT` eviction policy will remove a Session object from the cache as soon as the last simultaneous request referencing it exits.
+Alternatively, the `EVICT_ON_INACTIVITY` policy will remove a Session object from the cache after a configurable amount of time has passed without a request referencing it.
+
+If your Sessions are very long lived and infrequently referenced, you might use the `EVICT_ON_INACTIVITY_POLICY` to control the size of the cache.
+
+If your Sessions are small, or relatively few or stable in number or they are read-mostly, then you might select the `NEVER_EVICT` policy.
+With this policy, Session objects will remain in the cache until they either expire or are explicitly invalidated.
+
+If you have a high likelihood of simultaneous requests for the same session object, then the `EVICT_ON_SESSION_EXIT` policy will ensure the Session object stays in the cache as long as it is needed.
+
+
+==== Clustering Without a Sticky Load Balancer
+
+Without a sticky load balancer requests for the same session may arrive on any node in the cluster.
+This means it is likely that the copy of the Session object in any `SessionCache` is likely to be out-of-date, as the Session was probably last accessed on a different node.
+In this case, your `choices` are to use either the `NullSessionCache` or to de-tune the `DefaultSessionCache`.
+If you use the NullSessionCache all Session object caching is avoided.
+This means that every time a request references a session it must be brought in from persistent storage.
+It also means that there can be no sharing of Session objects for multiple requests for the same session: each will have their own Session object.
+Furthermore, the outcome of session writes are indeterminate because the Servlet Specification does not mandate ACID transactions for sessions.
+
+If you use the `DefaultSessionCache`, there is a risk that the caches on some nodes will contain out-of-date Session information as simultaneous requests for the same session are scattered over the cluster.
+To mitigate this somewhat you can use the `EVICT_ON_SESSION_EXIT` eviction policy: this will ensure that the Session is removed from the cache as soon as the last simultaneous request for it exits.
+Again, due to the lack of Session transactionality, the ordering outcome of write operations cannot be guaranteed.
+As the Session is cached while at least one request is accessing it, it is possible for multiple simultaneous requests to share the same Session object.
+
+
+==== Handling corrupted or unloadable session data
+
+For various reasons it might not be possible for the `SessionDataStore` to re-read a stored session.
+One scenario is that the session stores a serialized object in it's attributes, and after a redeployment there in an incompatible class change.
+Using the setter `SessionCache.setRemoveUnloadableSessions(true)` will allow the `SessionDataStore` to delete the unreadable session from persistent storage.
+This can be useful from preventing the scavenger from continually generating errors on the same expired, but un-restorable, session.
diff --git a/jetty-documentation/src/main/asciidoc/administration/startup/startup-base-vs-home.adoc b/jetty-documentation/src/main/asciidoc/administration/startup/startup-base-vs-home.adoc
index 496c50c99c7..a5ae3e9784e 100644
--- a/jetty-documentation/src/main/asciidoc/administration/startup/startup-base-vs-home.adoc
+++ b/jetty-documentation/src/main/asciidoc/administration/startup/startup-base-vs-home.adoc
@@ -115,8 +115,8 @@ Jetty Environment:
Config Search Order:
--------------------
- ${jetty.base} -> /home/user/jetty-distribution-9.4.1.v20170120/demo-base
- ${jetty.home} -> /home/user/Desktop/jetty-distribution-9.4.1.v20170120
+ ${jetty.base} -> /home/user/jetty-distribution-{VERSION}/demo-base
+ ${jetty.home} -> /home/user/Desktop/jetty-distribution-{VERSION}
JVM Arguments:
--------------
@@ -152,26 +152,26 @@ Jetty Server Classpath:
Version Information on 42 entries in the classpath.
Note: order presented here is how they would appear on the classpath.
changes to the --module=name command line options will be reflected here.
- 0: {VERSION} | ${jetty.home}/lib/jetty-client-{VERSION}.jar
+ 0: {VERSION} | ${jetty.home}/lib/jetty-client-{VERSION}.jar
1: 1.4.1.v201005082020 | ${jetty.base}/lib/ext/javax.mail.glassfish-1.4.1.v201005082020.jar
- 2: {VERSION} | ${jetty.base}/lib/ext/test-mock-resources-{VERSION}.jar
+ 2: {VERSION} | ${jetty.base}/lib/ext/test-mock-resources-{VERSION}.jar
3: (dir) | ${jetty.home}/resources
4: 3.1.0 | ${jetty.home}/lib/servlet-api-3.1.jar
5: 3.1.RC0 | ${jetty.home}/lib/jetty-schemas-3.1.jar
- 6: {VERSION} | ${jetty.home}/lib/jetty-http-{VERSION}.jar
- 7: {VERSION} | ${jetty.home}/lib/jetty-continuation-{VERSION}.jar
- 8: {VERSION} | ${jetty.home}/lib/jetty-server-{VERSION}.jar
- 9: {VERSION} | ${jetty.home}/lib/jetty-xml-{VERSION}.jar
-10: {VERSION} | ${jetty.home}/lib/jetty-util-{VERSION}.jar
-11: {VERSION} | ${jetty.home}/lib/jetty-io-{VERSION}.jar
-12: {VERSION} | ${jetty.home}/lib/jetty-jaas-{VERSION}.jar
-13: {VERSION} | ${jetty.home}/lib/jetty-jndi-{VERSION}.jar
+ 6: {VERSION} | ${jetty.home}/lib/jetty-http-{VERSION}.jar
+ 7: {VERSION} | ${jetty.home}/lib/jetty-continuation-{VERSION}.jar
+ 8: {VERSION} | ${jetty.home}/lib/jetty-server-{VERSION}.jar
+ 9: {VERSION} | ${jetty.home}/lib/jetty-xml-{VERSION}.jar
+10: {VERSION} | ${jetty.home}/lib/jetty-util-{VERSION}.jar
+11: {VERSION} | ${jetty.home}/lib/jetty-io-{VERSION}.jar
+12: {VERSION} | ${jetty.home}/lib/jetty-jaas-{VERSION}.jar
+13: {VERSION} | ${jetty.home}/lib/jetty-jndi-{VERSION}.jar
14: 1.1.0.v201105071233 | ${jetty.home}/lib/jndi/javax.activation-1.1.0.v201105071233.jar
15: 1.4.1.v201005082020 | ${jetty.home}/lib/jndi/javax.mail.glassfish-1.4.1.v201005082020.jar
16: 1.2 | ${jetty.home}/lib/jndi/javax.transaction-api-1.2.jar
-17: {VERSION} | ${jetty.home}/lib/jetty-rewrite-{VERSION}.jar
-18: {VERSION} | ${jetty.home}/lib/jetty-security-{VERSION}.jar
-19: {VERSION} | ${jetty.home}/lib/jetty-servlet-{VERSION}.jar
+17: {VERSION} | ${jetty.home}/lib/jetty-rewrite-{VERSION}.jar
+18: {VERSION} | ${jetty.home}/lib/jetty-security-{VERSION}.jar
+19: {VERSION} | ${jetty.home}/lib/jetty-servlet-{VERSION}.jar
20: 3.0.0 | ${jetty.home}/lib/jsp/javax.el-3.0.0.jar
21: 1.2.0.v201105211821 | ${jetty.home}/lib/jsp/javax.servlet.jsp.jstl-1.2.0.v201105211821.jar
22: 2.3.2 | ${jetty.home}/lib/jsp/javax.servlet.jsp-2.3.2.jar
@@ -179,21 +179,21 @@ Note: order presented here is how they would appear on the classpath.
24: 2.3.3 | ${jetty.home}/lib/jsp/jetty-jsp-jdt-2.3.3.jar
25: 1.2.0.v201112081803 | ${jetty.home}/lib/jsp/org.apache.taglibs.standard.glassfish-1.2.0.v201112081803.jar
26: 3.8.2.v20130121-145325 | ${jetty.home}/lib/jsp/org.eclipse.jdt.core-3.8.2.v20130121.jar
-27: {VERSION} | ${jetty.home}/lib/jetty-plus-{VERSION}.jar
-28: {VERSION} | ${jetty.home}/lib/jetty-webapp-{VERSION}.jar
-29: {VERSION} | ${jetty.home}/lib/jetty-annotations-{VERSION}.jar
+27: {VERSION} | ${jetty.home}/lib/jetty-plus-{VERSION}.jar
+28: {VERSION} | ${jetty.home}/lib/jetty-webapp-{VERSION}.jar
+29: {VERSION} | ${jetty.home}/lib/jetty-annotations-{VERSION}.jar
30: 4.1 | ${jetty.home}/lib/annotations/asm-4.1.jar
31: 4.1 | ${jetty.home}/lib/annotations/asm-commons-4.1.jar
32: 1.2 | ${jetty.home}/lib/annotations/javax.annotation-api-1.2.jar
-33: {VERSION} | ${jetty.home}/lib/jetty-deploy-{VERSION}.jar
+33: {VERSION} | ${jetty.home}/lib/jetty-deploy-{VERSION}.jar
34: 1.0 | ${jetty.home}/lib/websocket/javax.websocket-api-1.0.jar
-35: {VERSION} | ${jetty.home}/lib/websocket/javax-websocket-client-impl-{VERSION}.jar
-36: {VERSION} | ${jetty.home}/lib/websocket/javax-websocket-server-impl-{VERSION}.jar
-37: {VERSION} | ${jetty.home}/lib/websocket/websocket-api-{VERSION}.jar
-38: {VERSION} | ${jetty.home}/lib/websocket/websocket-client-{VERSION}.jar
-39: {VERSION} | ${jetty.home}/lib/websocket/websocket-common-{VERSION}.jar
-40: {VERSION} | ${jetty.home}/lib/websocket/websocket-server-{VERSION}.jar
-41: {VERSION} | ${jetty.home}/lib/websocket/websocket-servlet-{VERSION}.jar
+35: {VERSION} | ${jetty.home}/lib/websocket/javax-websocket-client-impl-{VERSION}.jar
+36: {VERSION} | ${jetty.home}/lib/websocket/javax-websocket-server-impl-{VERSION}.jar
+37: {VERSION} | ${jetty.home}/lib/websocket/websocket-api-{VERSION}.jar
+38: {VERSION} | ${jetty.home}/lib/websocket/websocket-client-{VERSION}.jar
+39: {VERSION} | ${jetty.home}/lib/websocket/websocket-common-{VERSION}.jar
+40: {VERSION} | ${jetty.home}/lib/websocket/websocket-server-{VERSION}.jar
+41: {VERSION} | ${jetty.home}/lib/websocket/websocket-servlet-{VERSION}.jar
Jetty Active XMLs:
------------------
diff --git a/jetty-documentation/src/main/asciidoc/administration/tuning/garbage-collection.adoc b/jetty-documentation/src/main/asciidoc/administration/tuning/garbage-collection.adoc
index d43818107e5..a5fcf49f10f 100644
--- a/jetty-documentation/src/main/asciidoc/administration/tuning/garbage-collection.adoc
+++ b/jetty-documentation/src/main/asciidoc/administration/tuning/garbage-collection.adoc
@@ -17,57 +17,39 @@
[[garbage-collection]]
=== Garbage Collection
-Tuning the JVM garbage collection (GC) can greatly improve Jetty performance.
-Specifically, you can avoid pauses while the system performs full garbage collections.
-Optimal tuning of the GC depends on the behavior of the application and requires detailed analysis, however there are general recommendations.
+Tuning the JVM garbage collection (GC) can greatly improve the performance of the JVM where Jetty and your application are running.
+Optimal tuning of the GC depends on the behavior of the application(s) and requires detailed analysis, but there are general recommendations to follow to at least obtain comprehensive GC logs that can be later analyzed.
-See official https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/[Java 8 Garbage Collection documentation] for further assistance.
+See official https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/[Java 8] and https://docs.oracle.com/javase/9/gctuning/introduction-garbage-collection-tuning.htm[Java 9] Garbage Collection documentation for further assistance.
-[[tuning-examples]]
-==== Tuning Examples
+[[garbage-collection-logging-configuration]]
+==== Garbage Collection Logging Configuration
-These options are general to the Oracle JVM, and work in a Java 8 installation.
-They provide good information about how your JVM is running; based on that initial information, you can then tune more finely.
-
-The most important thing you can do for yourself when considering GC is to capture the GC activity so that you can analyze what is happening and how often it happens.
+These options are general to OpenJDK (and therefore also for the Oracle JVM).
+They provide good information about the GC activity of your JVM, producing logs that can later be analyzed to perform finer tuning.
+.JDK 8 Garbage Collection Logging Configuration
[source,screen, subs="{sub-order}"]
....
--verbose:gc
-Xloggc:/path/to/myjettybase/logs/gc.log
-XX:+PrintGCDateStamps
--XX:+PrintGCTimeStamps
-XX:+PrintGCDetails
--XX:+PrintTenuringDistribution
--XX:+PrintCommandLineFlags
+-XX:+ParallelRefProcEnabled
-XX:+PrintReferenceGC
+-XX:+PrintTenuringDistribution
-XX:+PrintAdaptiveSizePolicy
....
-There are not many recommended options for GC that can apply to nearly all users.
+.JDK 9 Garbage Collection Logging Configuration
+[source,screen, subs="{sub-order}"]
+....
+Xlog:gc*,ergo*=trace,ref*=debug,age*=trace:file=/path/to/myjettybase/logs/gc.log:time,level,tags
+....
+There are not many recommended options for GC that can apply to all users.
However, the most obvious one is to disable explicit GC (this is performed regularly by RMI and can introduce an abnormal amount of GC pauses).
[source,screen, subs="{sub-order}"]
....
-XX:+DisableExplicitGC
....
-
-Before you apply any other GC tuning options, monitor your GC logs to see if https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html[tuning the CMS] makes sense for your environment.
-
-A common setup for those just starting out with GC tuning is included below for reference.
-
-____
-[CAUTION]
-This example configuration below could have a negative effect on your application performance, so continued monitoring of your GC log before and after is highly recommended to know if the configuration was beneficial or not.
-____
-
-[source,screen, subs="{sub-order}"]
-....
--XX:MaxGCPauseMillis=250
--XX:+UseConcMarkSweepGC
--XX:ParallelCMSThreads=2
--XX:+CMSClassUnloadingEnabled
--XX:+UseCMSCompactAtFullCollection
--XX:CMSInitiatingOccupancyFraction=80
-....
diff --git a/jetty-documentation/src/main/asciidoc/configuring/connectors/configuring-ssl.adoc b/jetty-documentation/src/main/asciidoc/configuring/connectors/configuring-ssl.adoc
index fd749015d61..c2ecdc26aef 100644
--- a/jetty-documentation/src/main/asciidoc/configuring/connectors/configuring-ssl.adoc
+++ b/jetty-documentation/src/main/asciidoc/configuring/connectors/configuring-ssl.adoc
@@ -20,7 +20,7 @@
This document provides an overview of how to configure SSL and TLS for Jetty.
[[configuring-jetty-for-ssl]]
-===== Configuring Jetty for SSL
+==== Configuring Jetty for SSL
To configure Jetty for SSL, complete the tasks in the following sections:
diff --git a/jetty-documentation/src/main/asciidoc/configuring/security/serving-aliased-files.adoc b/jetty-documentation/src/main/asciidoc/configuring/security/serving-aliased-files.adoc
index 35030473996..3fb6e507d7c 100644
--- a/jetty-documentation/src/main/asciidoc/configuring/security/serving-aliased-files.adoc
+++ b/jetty-documentation/src/main/asciidoc/configuring/security/serving-aliased-files.adoc
@@ -60,8 +60,8 @@ Unfortunately this approach denies all aliases, including symbolic links, which
==== Serving Aliases and Symbolic Links
Not all aliases are bad nor should be seen as attempts to subvert security constraints.
-Specifically symbolic links can be very useful when assembling complex web applications, yet by default Jetty will not serve them.
-Thus Jetty contexts support an extensible `AliasCheck` mechanism to allow aliases resources to be inspected an conditionally served.
+Specifically, symbolic links can be very useful when assembling complex web applications.
+As such, Jetty contexts support an extensible `AliasCheck` mechanism to allow aliases resources to be inspected and conditionally served.
In this way, "good" aliases can be detected and served.
Jetty provides several utility implementations of the `AliasCheck` interface as nested classes with `ContextHandler`:
@@ -70,6 +70,11 @@ ApproveAliases::
AllowSymLinkAliasChecker::
Approve Aliases using the java-7 `Files.readSymbolicLink(path)` and `Path.toRealPath(...)` APIs to check that aliases are valid symbolic links.
+____
+[NOTE]
+By default, Jetty serves aliased files for implementations running on UNIX as Contexts are created with both the {JDURL}/org/eclipse/jetty/server/handler/AllowSymLinkAliasChecker.html[`AllowSymLinkAliasChecker`] and {JDURL}/org/eclipse/jetty/server/handler/ContextHandler.ApproveNonExistentDirectoryAliases.html[`ApproveNonExistentDirectoryAliases`] alias checkers.
+____
+
An application is free to implement its own Alias checking.
Alias Checkers can be installed in a context via the following XML used in a context deployer file or `WEB-INF/jetty-web.xml`:
diff --git a/jetty-documentation/src/main/asciidoc/quick-start/getting-started/jetty-running.adoc b/jetty-documentation/src/main/asciidoc/quick-start/getting-started/jetty-running.adoc
index 99694c15ef0..3d579bc0bf8 100644
--- a/jetty-documentation/src/main/asciidoc/quick-start/getting-started/jetty-running.adoc
+++ b/jetty-documentation/src/main/asciidoc/quick-start/getting-started/jetty-running.adoc
@@ -26,8 +26,8 @@ To start Jetty on the default port of 8080, run the following command:
2015-06-04 10:50:44.806:INFO::main: Logging initialized @334ms
2015-06-04 10:50:44.858:WARN:oejs.HomeBaseWarning:main: This instance of Jetty is not running from a separate {jetty.base} directory, this is not recommended. See documentation at http://www.eclipse.org/jetty/documentation/current/startup.html
-2015-06-04 10:50:44.995:INFO:oejs.Server:main: jetty-9.3.0.v20150601
-2015-06-04 10:50:45.012:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///opt/jetty-distribution-9.3.0.v20150601/webapps/] at interval 1
+2015-06-04 10:50:44.995:INFO:oejs.Server:main: jetty-{VERSION}
+2015-06-04 10:50:45.012:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///opt/jetty-distribution-{VERSION}/webapps/] at interval 1
2015-06-04 10:50:45.030:INFO:oejs.ServerConnector:main: Started ServerConnector@19dfb72a{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
2015-06-04 10:50:45.030:INFO:oejs.Server:main: Started @558ms
----
@@ -57,13 +57,13 @@ Within the standard Jetty distribution there is the `demo-base` directory, which
2017-08-16 16:55:15.571:INFO::main: Logging initialized @521ms to org.eclipse.jetty.util.log.StdErrLog
2017-08-16 16:55:15.907:WARN::main: demo test-realm is deployed. DO NOT USE IN PRODUCTION!
-2017-08-16 16:55:15.910:INFO:oejs.Server:main: jetty-9.4.7-SNAPSHOT
-2017-08-16 16:55:15.931:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///tmp/jetty-distribution-9.4.7-SNAPSHOT/demo-base/webapps/] at interval 1
+2017-08-16 16:55:15.910:INFO:oejs.Server:main: jetty-{VERSION}
+2017-08-16 16:55:15.931:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///tmp/jetty-distribution-{VERSION}/demo-base/webapps/] at interval 1
2017-08-16 16:55:16.151:INFO:oeja.AnnotationConfiguration:main: Scanning elapsed time=50ms
2017-08-16 16:55:16.369:INFO:oejs.session:main: DefaultSessionIdManager workerName=node0
2017-08-16 16:55:16.369:INFO:oejs.session:main: No SessionScavenger set, using defaults
2017-08-16 16:55:16.370:INFO:oejs.session:main: Scavenging every 660000ms
-2017-08-16 16:55:16.416:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@7113b13f{/,file:///tmp/jetty-distribution-9.4.7-SNAPSHOT/demo-base/webapps/ROOT/,AVAILABLE}{/ROOT}
+2017-08-16 16:55:16.416:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@7113b13f{/,file:///tmp/jetty-distribution-{VERSION}/demo-base/webapps/ROOT/,AVAILABLE}{/ROOT}
2017-08-16 16:55:16.625:INFO:oeja.AnnotationConfiguration:main: Scanning elapsed time=82ms
2017-08-16 16:55:16.631:WARN::main: test webapp is deployed. DO NOT USE IN PRODUCTION!
2017-08-16 16:55:16.751:INFO:oejsh.ManagedAttributeListener:main: update PushFilter null->org.eclipse.jetty.servlets.PushCacheFilter@1a677343 on o.e.j.w.WebAppContext@2d7275fc{/test,file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-test.war-_test-any-7157753932050220016.dir/webapp/,STARTING}{/test.war}
@@ -72,7 +72,7 @@ Within the standard Jetty distribution there is the `demo-base` directory, which
2017-08-16 16:55:16.809:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@2d7275fc{/test,file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-test.war-_test-any-7157753932050220016.dir/webapp/,AVAILABLE}{/test.war}
2017-08-16 16:55:16.816:INFO:oejsh.ContextHandler:main: Started o.e.j.s.h.MovedContextHandler@7c9d8e2{/oldContextPath,null,AVAILABLE}
2017-08-16 16:55:16.854:INFO:oeja.AnnotationConfiguration:main: Scanning elapsed time=23ms
-2017-08-16 16:55:16.891:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@69453e37{/doc,file:///tmp/jetty-distribution-9.4.7-SNAPSHOT/demo-base/webapps/doc/,AVAILABLE}{/doc}
+2017-08-16 16:55:16.891:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@69453e37{/doc,file:///tmp/jetty-distribution-{VERSION}/demo-base/webapps/doc/,AVAILABLE}{/doc}
2017-08-16 16:55:16.942:INFO:oeja.AnnotationConfiguration:main: Scanning elapsed time=25ms
2017-08-16 16:55:16.945:WARN::main: test-jaas webapp is deployed. DO NOT USE IN PRODUCTION!
2017-08-16 16:55:16.983:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@4e3958e7{/test-jaas,file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-test-jaas.war-_test-jaas-any-6953571893682159674.dir/webapp/,AVAILABLE}{/test-jaas.war}
@@ -81,15 +81,15 @@ Within the standard Jetty distribution there is the `demo-base` directory, which
2017-08-16 16:55:17.192:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@1d8bd0de{/test-jndi,file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-test-jndi.war-_test-jndi-any-1246461885510956986.dir/webapp/,AVAILABLE}{/test-jndi.war}
2017-08-16 16:55:17.307:INFO:oeja.AnnotationConfiguration:main: Scanning elapsed time=53ms
2017-08-16 16:55:17.310:WARN::main: test-spec webapp is deployed. DO NOT USE IN PRODUCTION!
-2017-08-16 16:55:17.388:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@51dcb805{/test-spec,[file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-test-spec.war-_test-spec-any-3750193079644252256.dir/webapp/, jar:file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-test-spec.war-_test-spec-any-3750193079644252256.dir/webapp/WEB-INF/lib/test-web-fragment-9.4.7-SNAPSHOT.jar!/META-INF/resources],AVAILABLE}{/test-spec.war}
+2017-08-16 16:55:17.388:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@51dcb805{/test-spec,[file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-test-spec.war-_test-spec-any-3750193079644252256.dir/webapp/, jar:file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-test-spec.war-_test-spec-any-3750193079644252256.dir/webapp/WEB-INF/lib/test-web-fragment-{VERSION}.jar!/META-INF/resources],AVAILABLE}{/test-spec.war}
2017-08-16 16:55:17.490:INFO:oeja.AnnotationConfiguration:main: Scanning elapsed time=53ms
2017-08-16 16:55:17.493:WARN::main: async-rest webapp is deployed. DO NOT USE IN PRODUCTION!
-2017-08-16 16:55:17.516:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@1de76cc7{/async-rest,[file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-async-rest.war-_async-rest-any-8972552397332323832.dir/webapp/, jar:file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-async-rest.war-_async-rest-any-8972552397332323832.dir/webapp/WEB-INF/lib/example-async-rest-jar-9.4.7-SNAPSHOT.jar!/META-INF/resources],AVAILABLE}{/async-rest.war}
+2017-08-16 16:55:17.516:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@1de76cc7{/async-rest,[file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-async-rest.war-_async-rest-any-8972552397332323832.dir/webapp/, jar:file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-async-rest.war-_async-rest-any-8972552397332323832.dir/webapp/WEB-INF/lib/example-async-rest-jar-{VERSION}.jar!/META-INF/resources],AVAILABLE}{/async-rest.war}
2017-08-16 16:55:17.643:INFO:oeja.AnnotationConfiguration:main: Scanning elapsed time=83ms
2017-08-16 16:55:17.921:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@242b836{/proxy,file:///private/var/folders/h6/yb_lbnnn11g0y1jjlvqg631h0000gn/T/jetty-0.0.0.0-8080-javadoc-proxy.war-_javadoc-proxy-any-4521643038409884891.dir/webapp/,AVAILABLE}{/javadoc-proxy.war}
2017-08-16 16:55:17.936:INFO:oejs.AbstractConnector:main: Started ServerConnector@6f15d60e{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
-2017-08-16 16:55:17.944:INFO:oejus.SslContextFactory:main: x509=X509@58e1d9d(jetty,h=[jetty.eclipse.org],w=[]) for SslContextFactory@446a1e84(file:///tmp/jetty-distribution-9.4.7-SNAPSHOT/demo-base/etc/keystore,file:///tmp/jetty-distribution-9.4.7-SNAPSHOT/demo-base/etc/keystore)
-2017-08-16 16:55:17.944:INFO:oejus.SslContextFactory:main: x509=X509@4f0f2942(mykey,h=[],w=[]) for SslContextFactory@446a1e84(file:///tmp/jetty-distribution-9.4.7-SNAPSHOT/demo-base/etc/keystore,file:///tmp/jetty-distribution-9.4.7-SNAPSHOT/demo-base/etc/keystore)
+2017-08-16 16:55:17.944:INFO:oejus.SslContextFactory:main: x509=X509@58e1d9d(jetty,h=[jetty.eclipse.org],w=[]) for SslContextFactory@446a1e84(file:///tmp/jetty-distribution-{VERSION}/demo-base/etc/keystore,file:///tmp/jetty-distribution-{VERSION}/demo-base/etc/keystore)
+2017-08-16 16:55:17.944:INFO:oejus.SslContextFactory:main: x509=X509@4f0f2942(mykey,h=[],w=[]) for SslContextFactory@446a1e84(file:///tmp/jetty-distribution-{VERSION}/demo-base/etc/keystore,file:///tmp/jetty-distribution-{VERSION}/demo-base/etc/keystore)
2017-08-16 16:55:18.071:INFO:oejs.AbstractConnector:main: Started ServerConnector@41488b16{SSL,[ssl, http/1.1]}{0.0.0.0:8443}
2017-08-16 16:55:18.072:INFO:oejs.Server:main: Started @3022ms
----
@@ -179,7 +179,7 @@ INFO: Base directory was modified
2015-06-04 11:10:16.460:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///tmp/mybase/webapps/] at interval 1
2015-06-04 11:10:16.581:WARN::main: async-rest webapp is deployed. DO NOT USE IN PRODUCTION!
2015-06-04 11:10:16.589:INFO:oejw.StandardDescriptorProcessor:main: NO JSP Support for /, did not find org.eclipse.jetty.jsp.JettyJspServlet
-2015-06-04 11:10:16.628:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@1a407d53{/,[file:///tmp/jetty-0.0.0.0-8080-ROOT.war-_-any-4510228025526425427.dir/webapp/, jar:file:///tmp/jetty-0.0.0.0-8080-ROOT.war-_-any-4510228025526425427.dir/webapp/WEB-INF/lib/example-async-rest-jar-9.3.0.v20150601.jar!/META-INF/resources],AVAILABLE}{/ROOT.war}
+2015-06-04 11:10:16.628:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@1a407d53{/,[file:///tmp/jetty-0.0.0.0-8080-ROOT.war-_-any-4510228025526425427.dir/webapp/, jar:file:///tmp/jetty-0.0.0.0-8080-ROOT.war-_-any-4510228025526425427.dir/webapp/WEB-INF/lib/example-async-rest-jar-{VERSION}.jar!/META-INF/resources],AVAILABLE}{/ROOT.war}
2015-06-04 11:10:16.645:INFO:oejs.ServerConnector:main: Started ServerConnector@3abbfa04{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
2015-06-04 11:10:16.646:INFO:oejs.Server:main: Started @634ms
----
diff --git a/jetty-documentation/src/main/asciidoc/reference/architecture/jetty-classloading.adoc b/jetty-documentation/src/main/asciidoc/reference/architecture/jetty-classloading.adoc
index 5fcec472e60..8e66e7cc8a0 100644
--- a/jetty-documentation/src/main/asciidoc/reference/architecture/jetty-classloading.adoc
+++ b/jetty-documentation/src/main/asciidoc/reference/architecture/jetty-classloading.adoc
@@ -86,7 +86,7 @@ The default system classes are:
.Default System Classes
[width="100%",cols="8%,92%",options="header",]
|=======================================================================
-|System Classes
+|System Classes | Note
|java. |Java SE classes (per servlet spec v2.5 / SRV.9.7.2).
|javax. |Java SE classes (per servlet spec v2.5 / SRV.9.7.2).
|org.xml. |Needed by javax.xml.
@@ -98,7 +98,7 @@ The default system classes are:
|org.eclipse.jetty.servlet.DefaultServlet |Webapp can see and not change default servlet.
|=======================================================================
-Absolute classname can be passed, names ending with . are treated as packages names, and names starting with - are treated as negative matches and must be listed before any enclosing packages.
+Absolute classname can be passed, names ending with `.` are treated as packages names, and names starting with `-` are treated as negative matches and must be listed before any enclosing packages.
[[setting-server-classes]]
===== Setting Server Classes
diff --git a/jetty-gcloud/jetty-gcloud-session-manager/pom.xml b/jetty-gcloud/jetty-gcloud-session-manager/pom.xml
index 7cfb6decb80..ad6d81adb8a 100644
--- a/jetty-gcloud/jetty-gcloud-session-manager/pom.xml
+++ b/jetty-gcloud/jetty-gcloud-session-manager/pom.xml
@@ -1,5 +1,7 @@
-
+org.eclipse.jetty.gcloudgcloud-parent
@@ -22,12 +24,12 @@
${gcloud.version}
- javax.servlet
- servlet-api
+ javax.servlet
+ servlet-api
- javax.servlet
- javax.servlet-api
+ javax.servlet
+ javax.servlet-api
@@ -57,50 +59,51 @@