Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-10.0.x-5133-webappcontext-extraclasspath-cleanup
This commit is contained in:
commit
33286db44b
|
@ -39,12 +39,13 @@ pipeline {
|
|||
}
|
||||
}
|
||||
}
|
||||
stage( "Build / Test - JDK14" ) {
|
||||
|
||||
stage("Build / Test - JDK15") {
|
||||
agent { node { label 'linux' } }
|
||||
steps {
|
||||
container( 'jetty-build' ) {
|
||||
timeout( time: 120, unit: 'MINUTES' ) {
|
||||
mavenBuild( "jdk14", "-T3 clean install", "maven3", true )
|
||||
mavenBuild( "jdk15", "-T3 clean install", "maven3", true )
|
||||
warnings consoleParsers: [[parserName: 'Maven'], [parserName: 'Java']]
|
||||
junit testResults: '**/target/surefire-reports/*.xml,**/target/invoker-reports/TEST*.xml'
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables use of the apache implementation of JSP
|
||||
Enables use of the apache implementation of JSP.
|
||||
|
||||
[lib]
|
||||
lib/apache-jsp/*.jar
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables the apache version of JSTL
|
||||
Enables the apache version of JSTL for all webapps.
|
||||
|
||||
[lib]
|
||||
lib/apache-jstl/*.jar
|
||||
|
|
|
@ -31,7 +31,7 @@ import org.slf4j.LoggerFactory;
|
|||
* <ul>
|
||||
* <li>The <code>jetty.home</code> system property</li>
|
||||
* <li>The <code>JETTY_HOME</code> environment variable</li>
|
||||
* <li>The working directory hierarchy with subdirectory <code>jetty-distribution/target/home</code></li>
|
||||
* <li>The working directory hierarchy with subdirectory <code>jetty-distribution/target/distribution</code></li>
|
||||
* </ul>
|
||||
*/
|
||||
public class JettyDistribution
|
||||
|
@ -41,12 +41,23 @@ public class JettyDistribution
|
|||
|
||||
static
|
||||
{
|
||||
Path distro = asJettyDistribution(System.getProperty("jetty.home"));
|
||||
LOG.debug("JettyDistribution(prop(jetty.home)) = " + distro);
|
||||
if (distro == null)
|
||||
Path jettyHome = asDirectory(System.getProperty("jetty.home"));
|
||||
LOG.debug("JettyDistribution(prop(jetty.home)) = {}", jettyHome);
|
||||
if (jettyHome == null)
|
||||
{
|
||||
distro = asJettyDistribution(System.getenv().get("JETTY_HOME"));
|
||||
LOG.debug("JettyDistribution(env(JETTY_HOME)) = " + distro);
|
||||
jettyHome = asDirectory(System.getenv().get("JETTY_HOME"));
|
||||
LOG.debug("JettyDistribution(env(JETTY_HOME)) = {}", jettyHome);
|
||||
}
|
||||
|
||||
Path distro = null;
|
||||
|
||||
if (jettyHome != null)
|
||||
{
|
||||
Path parent = jettyHome.getParent();
|
||||
if (hasDemoBase(parent))
|
||||
{
|
||||
distro = parent;
|
||||
}
|
||||
}
|
||||
|
||||
if (distro == null)
|
||||
|
@ -54,13 +65,20 @@ public class JettyDistribution
|
|||
try
|
||||
{
|
||||
Path working = Paths.get(System.getProperty("user.dir"));
|
||||
LOG.debug("JettyDistribution(prop(user.dir)) = " + working);
|
||||
while (distro == null && working != null)
|
||||
Path dir = null;
|
||||
LOG.debug("JettyDistribution(prop(user.dir)) = {}", working);
|
||||
while (dir == null && working != null)
|
||||
{
|
||||
distro = asJettyDistribution(working.resolve("jetty-distribution/target/distribution").toString());
|
||||
dir = asDirectory(working.resolve("jetty-distribution/target/distribution").toString());
|
||||
if (dir != null && hasDemoBase(dir))
|
||||
{
|
||||
distro = dir;
|
||||
}
|
||||
// try one parent up
|
||||
working = working.getParent();
|
||||
}
|
||||
LOG.debug("JettyDistribution(working.resolve(...)) = " + distro);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("JettyDistribution(working.resolve(...)) = {}", distro);
|
||||
}
|
||||
catch (Throwable th)
|
||||
{
|
||||
|
@ -74,47 +92,47 @@ public class JettyDistribution
|
|||
}
|
||||
else
|
||||
{
|
||||
LOG.debug("JettyDistribution() FOUND = " + distro);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("JettyDistribution() FOUND = {}", distro);
|
||||
}
|
||||
DISTRIBUTION = distro;
|
||||
}
|
||||
|
||||
private static Path asJettyDistribution(String jettyHome)
|
||||
private static boolean hasDemoBase(Path path)
|
||||
{
|
||||
Path demoBase = path.resolve("demo-base");
|
||||
return Files.exists(demoBase) && Files.isDirectory(demoBase);
|
||||
}
|
||||
|
||||
private static Path asDirectory(String path)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (jettyHome == null)
|
||||
if (path == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (StringUtil.isBlank(jettyHome))
|
||||
if (StringUtil.isBlank(path))
|
||||
{
|
||||
LOG.debug("asJettyDistribution {} is blank", jettyHome);
|
||||
LOG.debug("asDirectory {} is blank", path);
|
||||
return null;
|
||||
}
|
||||
|
||||
Path dir = Paths.get(jettyHome);
|
||||
Path dir = Paths.get(path);
|
||||
if (!Files.exists(dir))
|
||||
{
|
||||
LOG.debug("asJettyDistribution {} does not exist", jettyHome);
|
||||
LOG.debug("asDirectory {} does not exist", path);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!Files.isDirectory(dir))
|
||||
{
|
||||
LOG.debug("asJettyDistribution {} is not a directory", jettyHome);
|
||||
LOG.debug("asDirectory {} is not a directory", path);
|
||||
return null;
|
||||
}
|
||||
|
||||
Path demoBase = dir.resolve("demo-base");
|
||||
if (!Files.exists(demoBase) || !Files.isDirectory(demoBase))
|
||||
{
|
||||
LOG.debug("asJettyDistribution {} has no demo-base", jettyHome);
|
||||
return null;
|
||||
}
|
||||
|
||||
LOG.debug("asJettyDistribution {}", dir);
|
||||
LOG.debug("asDirectory {}", dir);
|
||||
return dir.toAbsolutePath();
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.eclipse.jetty.deploy.PropertiesConfigurationManager;
|
|||
import org.eclipse.jetty.deploy.bindings.DebugListenerBinding;
|
||||
import org.eclipse.jetty.deploy.providers.WebAppProvider;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.io.ConnectionStatistics;
|
||||
import org.eclipse.jetty.jmx.MBeanContainer;
|
||||
import org.eclipse.jetty.plus.webapp.EnvConfiguration;
|
||||
import org.eclipse.jetty.plus.webapp.PlusConfiguration;
|
||||
|
@ -45,7 +46,6 @@ import org.eclipse.jetty.server.HttpConnectionFactory;
|
|||
import org.eclipse.jetty.server.LowResourceMonitor;
|
||||
import org.eclipse.jetty.server.SecureRequestCustomizer;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnectionStatistics;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.SslConnectionFactory;
|
||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||
|
@ -66,13 +66,13 @@ public class LikeJettyXml
|
|||
public static Server createServer(int port, int securePort, boolean addDebugListener) throws Exception
|
||||
{
|
||||
// Path to as-built jetty-distribution directory
|
||||
Path jettyHomeBuild = JettyDistribution.get();
|
||||
Path jettyDistro = JettyDistribution.get();
|
||||
|
||||
// Find jetty home and base directories
|
||||
String homePath = System.getProperty("jetty.home", jettyHomeBuild.toString());
|
||||
String homePath = System.getProperty("jetty.home", jettyDistro.resolve("jetty-home").toString());
|
||||
Path homeDir = Paths.get(homePath);
|
||||
|
||||
String basePath = System.getProperty("jetty.base", homeDir.resolve("demo-base").toString());
|
||||
String basePath = System.getProperty("jetty.base", jettyDistro.resolve("demo-base").toString());
|
||||
Path baseDir = Paths.get(basePath);
|
||||
|
||||
// Configure jetty.home and jetty.base system properties
|
||||
|
@ -171,7 +171,7 @@ public class LikeJettyXml
|
|||
StatisticsHandler stats = new StatisticsHandler();
|
||||
stats.setHandler(server.getHandler());
|
||||
server.setHandler(stats);
|
||||
ServerConnectionStatistics.addToAllConnectors(server);
|
||||
server.addBeanToAllConnectors(new ConnectionStatistics());
|
||||
|
||||
// === Rewrite Handler
|
||||
RewriteHandler rewrite = new RewriteHandler();
|
||||
|
@ -181,7 +181,7 @@ public class LikeJettyXml
|
|||
rewrite.addRule(new ValidUrlRule());
|
||||
|
||||
// === jetty-requestlog.xml ===
|
||||
AsyncRequestLogWriter logWriter = new AsyncRequestLogWriter(jettyHome + "/logs/yyyy_mm_dd.request.log");
|
||||
AsyncRequestLogWriter logWriter = new AsyncRequestLogWriter(jettyBase + "/logs/yyyy_mm_dd.request.log");
|
||||
logWriter.setFilenameDateFormat("yyyy_MM_dd");
|
||||
logWriter.setRetainDays(90);
|
||||
logWriter.setTimeZone("GMT");
|
||||
|
|
|
@ -20,9 +20,9 @@ package org.eclipse.jetty.embedded;
|
|||
|
||||
import java.lang.management.ManagementFactory;
|
||||
|
||||
import org.eclipse.jetty.io.ConnectionStatistics;
|
||||
import org.eclipse.jetty.jmx.MBeanContainer;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnectionStatistics;
|
||||
import org.eclipse.jetty.servlet.DefaultServlet;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
|
||||
|
@ -45,7 +45,7 @@ public class OneServletContextJmxStats
|
|||
context.addServlet(DefaultServlet.class, "/");
|
||||
|
||||
// Add Connector Statistics tracking to all connectors
|
||||
ServerConnectionStatistics.addToAllConnectors(server);
|
||||
server.addBeanToAllConnectors(new ConnectionStatistics());
|
||||
return server;
|
||||
}
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFact
|
|||
catch (Throwable x)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Could not initialize " + processor, x);
|
||||
LOG.debug("Could not initialize {}", processor, x);
|
||||
failure.addSuppressed(x);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -112,7 +112,7 @@ public class ConscryptServerALPNProcessor implements ALPNProcessor.Server
|
|||
public void handshakeFailed(Event event, Throwable failure)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("TLS handshake failed " + alpnConnection, failure);
|
||||
LOG.debug("TLS handshake failed {}", alpnConnection, failure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ public class JDK9ServerALPNProcessor implements ALPNProcessor.Server, SslHandsha
|
|||
public void handshakeFailed(Event event, Throwable failure)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("TLS handshake failed " + alpnConnection, failure);
|
||||
LOG.debug("TLS handshake failed {}", alpnConnection, failure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,11 @@
|
|||
[description]
|
||||
Enables the ALPN (Application Layer Protocol Negotiation) TLS extension.
|
||||
|
||||
[tag]
|
||||
connector
|
||||
ssl
|
||||
internal
|
||||
|
||||
[depend]
|
||||
ssl
|
||||
alpn-impl/alpn-${java.version.platform}
|
||||
|
|
|
@ -480,10 +480,10 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
boolean timeout = !latch.await(getMaxScanWait(context), TimeUnit.SECONDS);
|
||||
long elapsedMs = TimeUnit.MILLISECONDS.convert(System.nanoTime() - start, TimeUnit.NANOSECONDS);
|
||||
|
||||
LOG.info("Annotation scanning elapsed time={}ms", elapsedMs);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Annotation scanning elapsed time={}ms", elapsedMs);
|
||||
for (ParserTask p : _parserTasks)
|
||||
{
|
||||
LOG.debug("Scanned {} in {}ms", p.getResource(), TimeUnit.MILLISECONDS.convert(p.getStatistic().getElapsed(), TimeUnit.NANOSECONDS));
|
||||
|
@ -612,7 +612,7 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
if (c.isAnnotation())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Registering annotation handler for " + c.getName());
|
||||
LOG.debug("Registering annotation handler for {}", c.getName());
|
||||
_containerInitializerAnnotationHandlers.add(new ContainerInitializerAnnotationHandler(initializer, c));
|
||||
}
|
||||
}
|
||||
|
@ -621,14 +621,14 @@ public class AnnotationConfiguration extends AbstractConfiguration
|
|||
{
|
||||
initializer = new ContainerInitializer(service, null);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("No classes in HandlesTypes on initializer " + service.getClass());
|
||||
LOG.debug("No classes in HandlesTypes on initializer {}", service.getClass());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
initializer = new ContainerInitializer(service, null);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("No HandlesTypes annotation on initializer " + service.getClass());
|
||||
LOG.debug("No HandlesTypes annotation on initializer {}", service.getClass());
|
||||
}
|
||||
|
||||
initializers.add(initializer);
|
||||
|
|
|
@ -70,8 +70,8 @@ import org.slf4j.LoggerFactory;
|
|||
public class AnnotationParser
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AnnotationParser.class);
|
||||
protected static int ASM_OPCODE_VERSION = Opcodes.ASM7; //compatibility of api
|
||||
protected static String ASM_OPCODE_VERSION_STR = "ASM7";
|
||||
protected static int ASM_OPCODE_VERSION = Opcodes.ASM8; //compatibility of api
|
||||
protected static String ASM_OPCODE_VERSION_STR = "ASM8";
|
||||
|
||||
/**
|
||||
* Map of classnames scanned and the first location from which scan occurred
|
||||
|
@ -122,6 +122,11 @@ public class AnnotationParser
|
|||
asmVersion = Opcodes.ASM7;
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
asmVersion = Opcodes.ASM8;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
LOG.warn("Unrecognized ASM version, assuming {}", ASM_OPCODE_VERSION_STR);
|
||||
|
@ -822,7 +827,7 @@ public class AnnotationParser
|
|||
catch (Exception ex)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Error scanning file " + file, ex);
|
||||
LOG.debug("Error scanning file {}", file, ex);
|
||||
me.add(new RuntimeException("Error scanning file " + file, ex));
|
||||
}
|
||||
}
|
||||
|
@ -993,7 +998,7 @@ public class AnnotationParser
|
|||
if (path.startsWith(".") || path.contains("/."))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Contains hidden dirs: " + path);
|
||||
LOG.debug("Contains hidden dirs: {}", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -111,14 +111,14 @@ public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationH
|
|||
//JavaEE Spec 5.2.3: Field cannot be static
|
||||
if (Modifier.isStatic(field.getModifiers()))
|
||||
{
|
||||
LOG.warn("Skipping Resource annotation on " + clazz.getName() + "." + field.getName() + ": cannot be static");
|
||||
LOG.warn("Skipping Resource annotation on {}.{}: cannot be static", clazz.getName(), field.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
//JavaEE Spec 5.2.3: Field cannot be final
|
||||
if (Modifier.isFinal(field.getModifiers()))
|
||||
{
|
||||
LOG.warn("Skipping Resource annotation on " + clazz.getName() + "." + field.getName() + ": cannot be final");
|
||||
LOG.warn("Skipping Resource annotation on {}.{}: cannot be final", clazz.getName(), field.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -179,7 +179,7 @@ public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationH
|
|||
//Check there is a JNDI entry for this annotation
|
||||
if (bound)
|
||||
{
|
||||
LOG.debug("Bound " + (mappedName == null ? name : mappedName) + " as " + name);
|
||||
LOG.debug("Bound {} as {}", (mappedName == null ? name : mappedName), name);
|
||||
// Make the Injection for it if the binding succeeded
|
||||
injection = new Injection(clazz, field, type, name, mappedName);
|
||||
injections.add(injection);
|
||||
|
@ -243,7 +243,7 @@ public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationH
|
|||
//JavaEE Spec 5.2.3: Method cannot be static
|
||||
if (Modifier.isStatic(method.getModifiers()))
|
||||
{
|
||||
LOG.warn("Skipping Resource annotation on " + clazz.getName() + "." + method.getName() + ": cannot be static");
|
||||
LOG.warn("Skipping Resource annotation on {}.{}: cannot be static", clazz.getName(), method.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -251,19 +251,19 @@ public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationH
|
|||
// only 1 parameter
|
||||
if (!method.getName().startsWith("set"))
|
||||
{
|
||||
LOG.warn("Skipping Resource annotation on " + clazz.getName() + "." + method.getName() + ": invalid java bean, does not start with 'set'");
|
||||
LOG.warn("Skipping Resource annotation on {}.{}: invalid java bean, does not start with 'set'", clazz.getName(), method.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
if (method.getParameterCount() != 1)
|
||||
{
|
||||
LOG.warn("Skipping Resource annotation on " + clazz.getName() + "." + method.getName() + ": invalid java bean, not single argument to method");
|
||||
LOG.warn("Skipping Resource annotation on {}.{}: invalid java bean, not single argument to method", clazz.getName(), method.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
if (Void.TYPE != method.getReturnType())
|
||||
{
|
||||
LOG.warn("Skipping Resource annotation on " + clazz.getName() + "." + method.getName() + ": invalid java bean, not void");
|
||||
LOG.warn("Skipping Resource annotation on {}.{}: invalid java bean, not void", clazz.getName(), method.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -332,7 +332,7 @@ public class ResourceAnnotationHandler extends AbstractIntrospectableAnnotationH
|
|||
|
||||
if (bound)
|
||||
{
|
||||
LOG.debug("Bound " + (mappedName == null ? name : mappedName) + " as " + name);
|
||||
LOG.debug("Bound {} as {}", (mappedName == null ? name : mappedName), name);
|
||||
// Make the Injection for it
|
||||
injection = new Injection(clazz, method, paramType, resourceType, name, mappedName);
|
||||
injections.add(injection);
|
||||
|
|
|
@ -45,7 +45,7 @@ public class ResourcesAnnotationHandler extends AbstractIntrospectableAnnotation
|
|||
Resource[] resArray = resources.value();
|
||||
if (resArray == null || resArray.length == 0)
|
||||
{
|
||||
LOG.warn("Skipping empty or incorrect Resources annotation on " + clazz.getName());
|
||||
LOG.warn("Skipping empty or incorrect Resources annotation on {}", clazz.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,8 @@ public class ResourcesAnnotationHandler extends AbstractIntrospectableAnnotation
|
|||
|
||||
if (!org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_context, name, mappedName))
|
||||
if (!org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_context.getServer(), name, mappedName))
|
||||
LOG.warn("Skipping Resources(Resource) annotation on " + clazz.getName() + " for name " + name + ": No resource bound at " + (mappedName == null ? name : mappedName));
|
||||
LOG.warn("Skipping Resources(Resource) annotation on {} for name {}: no resource bound at {}",
|
||||
clazz.getName(), name, (mappedName == null ? name : mappedName));
|
||||
}
|
||||
catch (NamingException e)
|
||||
{
|
||||
|
|
|
@ -65,17 +65,17 @@ public class RunAsAnnotationHandler extends AbstractIntrospectableAnnotationHand
|
|||
}
|
||||
}
|
||||
else
|
||||
LOG.warn("Bad value for @RunAs annotation on class " + clazz.getName());
|
||||
LOG.warn("Bad value for @RunAs annotation on class {}", clazz.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public void handleField(String className, String fieldName, int access, String fieldType, String signature, Object value, String annotation)
|
||||
{
|
||||
LOG.warn("@RunAs annotation not applicable for fields: " + className + "." + fieldName);
|
||||
LOG.warn("@RunAs annotation not applicable for fields: {}.{}", className, fieldName);
|
||||
}
|
||||
|
||||
public void handleMethod(String className, String methodName, int access, String params, String signature, String[] exceptions, String annotation)
|
||||
{
|
||||
LOG.warn("@RunAs annotation ignored on method: " + className + "." + methodName + " " + signature);
|
||||
LOG.warn("@RunAs annotation ignored on method: {}.{} {}", className, methodName, signature);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ public class ServletContainerInitializersStarter extends AbstractLifeCycle imple
|
|||
try
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Calling ServletContainerInitializer " + i.getTarget().getClass().getName());
|
||||
LOG.debug("Calling ServletContainerInitializer {}", i.getTarget().getClass().getName());
|
||||
i.callStartup(_context);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
|
@ -83,7 +83,7 @@ public class ServletSecurityAnnotationHandler extends AbstractIntrospectableAnno
|
|||
|
||||
if (constraintsExist(servletMappings, constraintMappings))
|
||||
{
|
||||
LOG.warn("Constraints already defined for " + clazz.getName() + ", skipping ServletSecurity annotation");
|
||||
LOG.warn("Constraints already defined for {}, skipping ServletSecurity annotation", clazz.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -62,14 +62,14 @@ public class WebFilterAnnotation extends DiscoveredAnnotation
|
|||
Class clazz = getTargetClass();
|
||||
if (clazz == null)
|
||||
{
|
||||
LOG.warn(_className + " cannot be loaded");
|
||||
LOG.warn("{} cannot be loaded", _className);
|
||||
return;
|
||||
}
|
||||
|
||||
//Servlet Spec 8.1.2
|
||||
if (!Filter.class.isAssignableFrom(clazz))
|
||||
{
|
||||
LOG.warn(clazz.getName() + " is not assignable from javax.servlet.Filter");
|
||||
LOG.warn("{} is not assignable from javax.servlet.Filter", clazz.getName());
|
||||
return;
|
||||
}
|
||||
MetaData metaData = _context.getMetaData();
|
||||
|
@ -78,7 +78,7 @@ public class WebFilterAnnotation extends DiscoveredAnnotation
|
|||
|
||||
if (filterAnnotation.value().length > 0 && filterAnnotation.urlPatterns().length > 0)
|
||||
{
|
||||
LOG.warn(clazz.getName() + " defines both @WebFilter.value and @WebFilter.urlPatterns");
|
||||
LOG.warn("{} defines both @WebFilter.value and @WebFilter.urlPatterns", clazz.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ public class WebFilterAnnotationHandler extends AbstractDiscoverableAnnotationHa
|
|||
{
|
||||
if (annotationName == null || !"javax.servlet.annotation.WebFilter".equals(annotationName))
|
||||
return;
|
||||
LOG.warn("@WebFilter not applicable for fields: " + info.getClassInfo().getClassName() + "." + info.getFieldName());
|
||||
LOG.warn("@WebFilter not applicable for fields: {}.{}", info.getClassInfo().getClassName(), info.getFieldName());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -60,6 +60,6 @@ public class WebFilterAnnotationHandler extends AbstractDiscoverableAnnotationHa
|
|||
{
|
||||
if (annotationName == null || !"javax.servlet.annotation.WebFilter".equals(annotationName))
|
||||
return;
|
||||
LOG.warn("@WebFilter not applicable for methods: " + info.getClassInfo().getClassName() + "." + info.getMethodName() + " " + info.getSignature());
|
||||
LOG.warn("@WebFilter not applicable for methods: {}.{} {}", info.getClassInfo().getClassName(), info.getMethodName(), info.getSignature());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ public class WebListenerAnnotation extends DiscoveredAnnotation
|
|||
|
||||
if (clazz == null)
|
||||
{
|
||||
LOG.warn(_className + " cannot be loaded");
|
||||
LOG.warn("{} cannot be loaded", _className);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ public class WebListenerAnnotation extends DiscoveredAnnotation
|
|||
}
|
||||
}
|
||||
else
|
||||
LOG.warn(clazz.getName() + " does not implement one of the servlet listener interfaces");
|
||||
LOG.warn("{} does not implement one of the servlet listener interfaces", clazz.getName());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
|
@ -49,7 +49,7 @@ public class WebListenerAnnotationHandler extends AbstractDiscoverableAnnotation
|
|||
{
|
||||
if (annotationName == null || !"javax.servlet.annotation.WebListener".equals(annotationName))
|
||||
return;
|
||||
LOG.warn("@WebListener is not applicable to fields: " + info.getClassInfo().getClassName() + "." + info.getFieldName());
|
||||
LOG.warn("@WebListener is not applicable to fields: {}.{}", info.getClassInfo().getClassName(), info.getFieldName());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -57,6 +57,6 @@ public class WebListenerAnnotationHandler extends AbstractDiscoverableAnnotation
|
|||
{
|
||||
if (annotationName == null || !"javax.servlet.annotation.WebListener".equals(annotationName))
|
||||
return;
|
||||
LOG.warn("@WebListener is not applicable to methods: " + info.getClassInfo().getClassName() + "." + info.getMethodName() + " " + info.getSignature());
|
||||
LOG.warn("@WebListener is not applicable to methods: {}.{} {}", info.getClassInfo().getClassName(), info.getMethodName(), info.getSignature());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,14 +65,14 @@ public class WebServletAnnotation extends DiscoveredAnnotation
|
|||
|
||||
if (clazz == null)
|
||||
{
|
||||
LOG.warn(_className + " cannot be loaded");
|
||||
LOG.warn("{} cannot be loaded", _className);
|
||||
return;
|
||||
}
|
||||
|
||||
//Servlet Spec 8.1.1
|
||||
if (!HttpServlet.class.isAssignableFrom(clazz))
|
||||
{
|
||||
LOG.warn(clazz.getName() + " is not assignable from javax.servlet.http.HttpServlet");
|
||||
LOG.warn("{} is not assignable from javax.servlet.http.HttpServlet", clazz.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ public class WebServletAnnotation extends DiscoveredAnnotation
|
|||
|
||||
if (annotation.urlPatterns().length > 0 && annotation.value().length > 0)
|
||||
{
|
||||
LOG.warn(clazz.getName() + " defines both @WebServlet.value and @WebServlet.urlPatterns");
|
||||
LOG.warn("{} defines both @WebServlet.value and @WebServlet.urlPatterns", clazz.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@ public class WebServletAnnotation extends DiscoveredAnnotation
|
|||
|
||||
if (urlPatterns.length == 0)
|
||||
{
|
||||
LOG.warn(clazz.getName() + " defines neither @WebServlet.value nor @WebServlet.urlPatterns");
|
||||
LOG.warn("{} defines neither @WebServlet.value nor @WebServlet.urlPatterns", clazz.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,14 +49,12 @@
|
|||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ant</groupId>
|
||||
<groupId>org.apache.ant</groupId>
|
||||
<artifactId>ant</artifactId>
|
||||
<version>1.6.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ant</groupId>
|
||||
<groupId>org.apache.ant</groupId>
|
||||
<artifactId>ant-launcher</artifactId>
|
||||
<version>1.6.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>flatten-maven-plugin</artifactId>
|
||||
<version>1.0.1</version>
|
||||
<version>1.2.5</version>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}</outputDirectory>
|
||||
<flattenedPomFilename>flattened-pom.xml</flattenedPomFilename>
|
||||
|
|
|
@ -41,6 +41,12 @@
|
|||
<artifactId>jetty-slf4j-impl</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.weld.servlet</groupId>
|
||||
<artifactId>weld-servlet-core</artifactId>
|
||||
<version>${weld.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Configures Jetty to use the "CdiDecoratingListener" as the default
|
||||
CDI integration mode that allows a webapp to register it's own CDI
|
||||
decorator.
|
||||
Configures Jetty to use the "CdiDecoratingListener" as the default CDI mode.
|
||||
This mode that allows a webapp to register it's own CDI decorator.
|
||||
|
||||
[tag]
|
||||
cdi
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Configures Jetty to use the "CdiSpiDecorator" that calls the CDI SPI
|
||||
as the default CDI integration mode.
|
||||
Configures Jetty to use the "CdiSpiDecorator" as the default CDI mode.
|
||||
This mode uses the CDI SPI to integrate an arbitrary CDI implementation.
|
||||
|
||||
[tag]
|
||||
cdi
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Support for CDI inside the webapp.
|
||||
Integration of CDI within webapp to Jetty container object lifecycles.
|
||||
This module does not provide CDI, but configures jetty to support various
|
||||
integration modes with a CDI implementation on the webapp classpath.
|
||||
CDI integration modes can be selected per webapp with the "org.eclipse.jetty.cdi"
|
||||
|
@ -27,8 +27,3 @@ etc/cdi/jetty-cdi.xml
|
|||
|
||||
[lib]
|
||||
lib/jetty-cdi-${jetty.version}.jar
|
||||
lib/apache-jsp/org.mortbay.jasper.apache-el-*.jar
|
||||
|
||||
[ini]
|
||||
jetty.webapp.addSystemClasses+=,org.eclipse.jetty.cdi.CdiServletContainerInitializer
|
||||
jetty.webapp.addServerClasses+=,-org.eclipse.jetty.cdi.CdiServletContainerInitializer
|
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.cdi;
|
||||
|
||||
import org.eclipse.jetty.annotations.AnnotationConfiguration;
|
||||
import org.eclipse.jetty.plus.webapp.PlusConfiguration;
|
||||
import org.eclipse.jetty.webapp.AbstractConfiguration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* <p>CDI Configuration</p>
|
||||
* <p>This configuration configures the WebAppContext server/system classes to
|
||||
* be able to see the {@link CdiServletContainerInitializer}.
|
||||
* </p>
|
||||
*/
|
||||
public class CdiConfiguration extends AbstractConfiguration
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(CdiConfiguration.class);
|
||||
|
||||
public CdiConfiguration()
|
||||
{
|
||||
protectAndExpose("org.eclipse.jetty.cdi.CdiServletContainerInitializer");
|
||||
addDependents(AnnotationConfiguration.class, PlusConfiguration.class);
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
|
|||
/**
|
||||
* A DecoratingListener that listens for "org.eclipse.jetty.cdi.decorator"
|
||||
*/
|
||||
class CdiDecoratingListener extends DecoratingListener
|
||||
public class CdiDecoratingListener extends DecoratingListener
|
||||
{
|
||||
public static final String MODE = "CdiDecoratingListener";
|
||||
public static final String ATTRIBUTE = "org.eclipse.jetty.cdi.decorator";
|
||||
|
@ -32,5 +32,6 @@ class CdiDecoratingListener extends DecoratingListener
|
|||
public CdiDecoratingListener(ServletContextHandler contextHandler)
|
||||
{
|
||||
super(contextHandler, ATTRIBUTE);
|
||||
contextHandler.setAttribute(CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, MODE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,14 +86,12 @@ public class CdiServletContainerInitializer implements ServletContainerInitializ
|
|||
default:
|
||||
throw new IllegalStateException(mode);
|
||||
}
|
||||
|
||||
context.setAttribute(CDI_INTEGRATION_ATTRIBUTE, mode);
|
||||
LOG.info(mode + " enabled in " + ctx);
|
||||
LOG.info("{} enabled in {}", mode, ctx);
|
||||
}
|
||||
catch (UnsupportedOperationException | ClassNotFoundException e)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("CDI not found in " + ctx, e);
|
||||
LOG.debug("CDI not found in {}", ctx, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,12 @@ package org.eclipse.jetty.cdi;
|
|||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.util.Decorator;
|
||||
|
@ -61,11 +65,15 @@ public class CdiSpiDecorator implements Decorator
|
|||
private final MethodHandle _inject;
|
||||
private final MethodHandle _dispose;
|
||||
private final MethodHandle _release;
|
||||
private final Set<String> _undecorated = new HashSet<>(Collections.singletonList("org.jboss.weld.environment.servlet.Listener"));
|
||||
|
||||
public CdiSpiDecorator(ServletContextHandler context) throws UnsupportedOperationException
|
||||
{
|
||||
_context = context;
|
||||
context.setAttribute(CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, MODE);
|
||||
ClassLoader classLoader = _context.getClassLoader();
|
||||
if (classLoader == null)
|
||||
classLoader = this.getClass().getClassLoader();
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -92,6 +100,54 @@ public class CdiSpiDecorator implements Decorator
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a class can be decorated.
|
||||
* The default implementation checks the set from {@link #getUndecoratable()}
|
||||
* on the class and all it's super classes.
|
||||
* @param clazz The class to check
|
||||
* @return True if the class and all it's super classes can be decorated
|
||||
*/
|
||||
protected boolean isDecoratable(Class<?> clazz)
|
||||
{
|
||||
if (Object.class == clazz)
|
||||
return true;
|
||||
if (getUndecoratable().contains(clazz.getName()))
|
||||
return false;
|
||||
return isDecoratable(clazz.getSuperclass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the set of classes that will not be decorated. The default set includes the listener from Weld that will itself
|
||||
* setup decoration.
|
||||
* @return The modifiable set of class names that will not be decorated (ie {@link #isDecoratable(Class)} will return false.
|
||||
* @see #isDecoratable(Class)
|
||||
*/
|
||||
public Set<String> getUndecoratable()
|
||||
{
|
||||
return _undecorated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param classnames The set of class names that will not be decorated.
|
||||
* @see #isDecoratable(Class)
|
||||
*/
|
||||
public void setUndecoratable(Set<String> classnames)
|
||||
{
|
||||
_undecorated.clear();
|
||||
if (classnames != null)
|
||||
_undecorated.addAll(classnames);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param classname A class name that will be added to the undecoratable classes set.
|
||||
* @see #getUndecoratable()
|
||||
* @see #isDecoratable(Class)
|
||||
*/
|
||||
public void addUndecoratable(String... classname)
|
||||
{
|
||||
_undecorated.addAll(Arrays.asList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorate an object.
|
||||
* <p>The signature of this method must match what is introspected for by the
|
||||
|
@ -108,11 +164,12 @@ public class CdiSpiDecorator implements Decorator
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("decorate {} in {}", o, _context);
|
||||
|
||||
_decorated.put(o, new Decorated(o));
|
||||
if (isDecoratable(o.getClass()))
|
||||
_decorated.put(o, new Decorated(o));
|
||||
}
|
||||
catch (Throwable th)
|
||||
{
|
||||
LOG.warn("Unable to decorate " + o, th);
|
||||
LOG.warn("Unable to decorate {}", o, th);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
@ -134,7 +191,7 @@ public class CdiSpiDecorator implements Decorator
|
|||
}
|
||||
catch (Throwable th)
|
||||
{
|
||||
LOG.warn("Unable to destroy " + o, th);
|
||||
LOG.warn("Unable to destroy {}", o, th);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
org.eclipse.jetty.cdi.CdiConfiguration
|
||||
|
|
@ -0,0 +1,331 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.embedded;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
import javax.enterprise.inject.Produces;
|
||||
import javax.enterprise.inject.spi.BeanManager;
|
||||
import javax.enterprise.inject.spi.InjectionPoint;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.annotations.AnnotationConfiguration;
|
||||
import org.eclipse.jetty.cdi.CdiConfiguration;
|
||||
import org.eclipse.jetty.cdi.CdiServletContainerInitializer;
|
||||
import org.eclipse.jetty.server.LocalConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ListenerHolder;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.webapp.Configuration;
|
||||
import org.eclipse.jetty.webapp.JettyWebXmlConfiguration;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
||||
public class EmbeddedWeldTest
|
||||
{
|
||||
public static Server createServerWithServletContext(String mode)
|
||||
{
|
||||
Server server = new Server();
|
||||
server.addConnector(new LocalConnector(server));
|
||||
ServletContextHandler context = new ServletContextHandler();
|
||||
context.setContextPath("/");
|
||||
context.setResourceBase("src/test/resources/weldtest");
|
||||
server.setHandler(context);
|
||||
|
||||
// Setup context
|
||||
context.addFilter(MyFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
|
||||
context.addServlet(GreetingsServlet.class, "/");
|
||||
context.getServletHandler().addListener(new ListenerHolder(MyContextListener.class));
|
||||
|
||||
// Setup Jetty weld integration
|
||||
switch (mode)
|
||||
{
|
||||
case "none" : // Do nothing, let weld work it out.
|
||||
// Expect:INFO: WELD-ENV-001201: Jetty 7.2+ detected, CDI injection will be available in Servlets and Filters. Injection into Listeners is not supported.
|
||||
context.getServletHandler().addListener(new ListenerHolder(org.jboss.weld.environment.servlet.Listener.class));
|
||||
break;
|
||||
|
||||
case "DecoratingListener+Listener":
|
||||
// Expect:INFO: WELD-ENV-001212: Jetty CdiDecoratingListener support detected, CDI injection will be available in Listeners, Servlets and Filters.
|
||||
context.addEventListener(new org.eclipse.jetty.webapp.DecoratingListener(context));
|
||||
context.getServletHandler().addListener(new ListenerHolder(org.jboss.weld.environment.servlet.Listener.class));
|
||||
break;
|
||||
|
||||
case "CdiDecoratingListener+Listener":
|
||||
// Expect:INFO: WELD-ENV-001212: Jetty CdiDecoratingListener support detected, CDI injection will be available in Listeners, Servlets and Filters.
|
||||
context.addEventListener(new org.eclipse.jetty.cdi.CdiDecoratingListener(context));
|
||||
context.addEventListener(new org.jboss.weld.environment.servlet.Listener());
|
||||
break;
|
||||
|
||||
case "CdiSpiDecorator+Listener":
|
||||
// Expect:INFO: WELD-ENV-001213: Jetty CDI SPI support detected, CDI injection will be available in Listeners, Servlets and Filters.
|
||||
context.getObjectFactory().addDecorator(new org.eclipse.jetty.cdi.CdiSpiDecorator(context));
|
||||
context.getServletHandler().addListener(new ListenerHolder(org.jboss.weld.environment.servlet.Listener.class));
|
||||
break;
|
||||
|
||||
case "CdiServletContainerInitializer+Listener":
|
||||
// Expect:INFO: WELD-ENV-001213: Jetty CDI SPI support detected, CDI injection will be available in Listeners, Servlets and Filters.
|
||||
context.addBean(new ServletContextHandler.Initializer(context, new org.eclipse.jetty.cdi.CdiServletContainerInitializer()));
|
||||
context.addEventListener(new org.jboss.weld.environment.servlet.Listener());
|
||||
break;
|
||||
|
||||
case "CdiServletContainerInitializer(CdiDecoratingListener)+Listener":
|
||||
// Expect:INFO: WELD-ENV-001212: Jetty CdiDecoratingListener support detected, CDI injection will be available in Listeners, Servlets and Filters
|
||||
context.setInitParameter(org.eclipse.jetty.cdi.CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, org.eclipse.jetty.cdi.CdiDecoratingListener.MODE);
|
||||
context.addBean(new ServletContextHandler.Initializer(context, new org.eclipse.jetty.cdi.CdiServletContainerInitializer()));
|
||||
context.addEventListener(new org.jboss.weld.environment.servlet.Listener());
|
||||
break;
|
||||
|
||||
case "CdiServletContainerInitializer+EnhancedListener":
|
||||
// Expect:INFO: WELD-ENV-001213: Jetty CDI SPI support detected, CDI injection will be available in Listeners, Servlets and Filters.
|
||||
context.addBean(new ServletContextHandler.Initializer(context, new org.eclipse.jetty.cdi.CdiServletContainerInitializer()));
|
||||
context.addBean(new ServletContextHandler.Initializer(context, new org.jboss.weld.environment.servlet.EnhancedListener()));
|
||||
break;
|
||||
|
||||
case "CdiServletContainerInitializer(CdiDecoratingListener)+EnhancedListener":
|
||||
// Expect:INFO: WELD-ENV-001212: Jetty CdiDecoratingListener support detected, CDI injection will be available in Listeners, Servlets and Filters
|
||||
context.setInitParameter(org.eclipse.jetty.cdi.CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, org.eclipse.jetty.cdi.CdiDecoratingListener.MODE);
|
||||
context.addBean(new ServletContextHandler.Initializer(context, new org.eclipse.jetty.cdi.CdiServletContainerInitializer()));
|
||||
context.addBean(new ServletContextHandler.Initializer(context, new org.jboss.weld.environment.servlet.EnhancedListener()));
|
||||
break;
|
||||
|
||||
case "EnhancedListener+CdiServletContainerInitializer(CdiDecoratingListener)":
|
||||
// Expect:INFO: WELD-ENV-001212: Jetty CdiDecoratingListener support detected, CDI injection will be available in Listeners, Servlets and Filters
|
||||
context.setInitParameter(org.eclipse.jetty.cdi.CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, org.eclipse.jetty.cdi.CdiDecoratingListener.MODE);
|
||||
context.addBean(new ServletContextHandler.Initializer(context, new org.jboss.weld.environment.servlet.EnhancedListener()));
|
||||
context.addBean(new ServletContextHandler.Initializer(context, new org.eclipse.jetty.cdi.CdiServletContainerInitializer()));
|
||||
break;
|
||||
}
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
@ParameterizedTest()
|
||||
@ValueSource(strings =
|
||||
{
|
||||
"none",
|
||||
"DecoratingListener+Listener",
|
||||
"CdiDecoratingListener+Listener",
|
||||
"CdiSpiDecorator+Listener",
|
||||
"CdiServletContainerInitializer+Listener",
|
||||
"CdiServletContainerInitializer(CdiDecoratingListener)+Listener",
|
||||
"CdiServletContainerInitializer+EnhancedListener",
|
||||
"CdiServletContainerInitializer(CdiDecoratingListener)+EnhancedListener"
|
||||
})
|
||||
public void testServletContext(String mode) throws Exception
|
||||
{
|
||||
Server server = createServerWithServletContext(mode);
|
||||
server.start();
|
||||
LocalConnector connector = server.getBean(LocalConnector.class);
|
||||
String response = connector.getResponse("GET / HTTP/1.0\r\n\r\n");
|
||||
assertThat(response, containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(response, containsString("Hello GreetingsServlet filtered by Weld BeanManager "));
|
||||
assertThat(response, containsString("Beans from Weld BeanManager "));
|
||||
if (mode.contains("EnhancedListener"))
|
||||
assertThat(response, containsString("Listener saw Weld BeanManager"));
|
||||
else
|
||||
assertThat(response, containsString("Listener saw null"));
|
||||
|
||||
assertThat(response, containsString("Beans from Weld BeanManager for "));
|
||||
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebappContext() throws Exception
|
||||
{
|
||||
Server server = new Server(8080);
|
||||
server.addConnector(new LocalConnector(server));
|
||||
WebAppContext webapp = new WebAppContext();
|
||||
webapp.setContextPath("/");
|
||||
webapp.setResourceBase("src/test/resources/weldtest");
|
||||
server.setHandler(webapp);
|
||||
|
||||
webapp.setInitParameter(org.eclipse.jetty.cdi.CdiServletContainerInitializer.CDI_INTEGRATION_ATTRIBUTE, org.eclipse.jetty.cdi.CdiDecoratingListener.MODE);
|
||||
webapp.addBean(new ServletContextHandler.Initializer(webapp, new org.eclipse.jetty.cdi.CdiServletContainerInitializer()));
|
||||
webapp.addBean(new ServletContextHandler.Initializer(webapp, new org.jboss.weld.environment.servlet.EnhancedListener()));
|
||||
|
||||
webapp.getServerClassMatcher().add("-org.eclipse.jetty.embedded.");
|
||||
webapp.getSystemClassMatcher().add("org.eclipse.jetty.embedded.");
|
||||
|
||||
webapp.addServlet(GreetingsServlet.class, "/");
|
||||
webapp.addFilter(MyFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
|
||||
webapp.getServletHandler().addListener(new ListenerHolder(MyContextListener.class));
|
||||
|
||||
server.start();
|
||||
|
||||
LocalConnector connector = server.getBean(LocalConnector.class);
|
||||
String response = connector.getResponse("GET / HTTP/1.0\r\n\r\n");
|
||||
assertThat(response, containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(response, containsString("Hello GreetingsServlet filtered by Weld BeanManager "));
|
||||
assertThat(response, containsString("Beans from Weld BeanManager "));
|
||||
assertThat(response, containsString("Listener saw Weld BeanManager"));
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWebappContextDiscovered() throws Exception
|
||||
{
|
||||
Server server = new Server(8080);
|
||||
server.addConnector(new LocalConnector(server));
|
||||
WebAppContext webapp = new WebAppContext();
|
||||
webapp.setContextPath("/");
|
||||
webapp.setResourceBase("src/test/resources/weldtest");
|
||||
server.setHandler(webapp);
|
||||
|
||||
// Need the AnnotationConfiguration to detect SCIs
|
||||
webapp.addConfiguration(new AnnotationConfiguration());
|
||||
|
||||
// Need to expose our SCI. This is ugly could be made better in jetty-10 with a CdiConfiguration
|
||||
webapp.addConfiguration(new CdiConfiguration());
|
||||
|
||||
// This is ugly but needed for maven for testing in a overlaid war pom
|
||||
webapp.getServerClassMatcher().add("-org.eclipse.jetty.embedded.");
|
||||
webapp.getSystemClassMatcher().add("org.eclipse.jetty.embedded.");
|
||||
|
||||
webapp.getServletHandler().addListener(new ListenerHolder(MyContextListener.class));
|
||||
webapp.addFilter(MyFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
|
||||
webapp.addServlet(GreetingsServlet.class, "/");
|
||||
|
||||
server.start();
|
||||
|
||||
LocalConnector connector = server.getBean(LocalConnector.class);
|
||||
String response = connector.getResponse("GET / HTTP/1.0\r\n\r\n");
|
||||
System.err.println(response);
|
||||
assertThat(response, containsString("HTTP/1.1 200 OK"));
|
||||
assertThat(response, containsString("Hello GreetingsServlet filtered by Weld BeanManager "));
|
||||
assertThat(response, containsString("Beans from Weld BeanManager "));
|
||||
assertThat(response, containsString("Listener saw Weld BeanManager"));
|
||||
server.stop();
|
||||
|
||||
}
|
||||
|
||||
public static class MyContextListener implements ServletContextListener
|
||||
{
|
||||
@Inject
|
||||
BeanManager manager;
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce)
|
||||
{
|
||||
sce.getServletContext().setAttribute("listener", manager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static class MyFilter implements Filter
|
||||
{
|
||||
@Inject
|
||||
BeanManager manager;
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException
|
||||
{
|
||||
if (manager == null)
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
|
||||
{
|
||||
// copy attribute from MyListener to see if it was decorated.
|
||||
request.setAttribute("filter", manager);
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static class GreetingsServlet extends HttpServlet
|
||||
{
|
||||
@Inject
|
||||
@Named("friendly")
|
||||
public Greetings greetings;
|
||||
|
||||
@Inject
|
||||
BeanManager manager;
|
||||
|
||||
@Override
|
||||
public void init()
|
||||
{
|
||||
if (manager == null)
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
|
||||
{
|
||||
resp.setContentType("text/plain");
|
||||
resp.getWriter().print(greetings == null ? "NULL" : greetings.getGreeting());
|
||||
resp.getWriter().print(" filtered by ");
|
||||
resp.getWriter().println(req.getAttribute("filter"));
|
||||
resp.getWriter().println("Beans from " + manager);
|
||||
resp.getWriter().println("Listener saw " + req.getServletContext().getAttribute("listener"));
|
||||
}
|
||||
}
|
||||
|
||||
public interface Greetings
|
||||
{
|
||||
String getGreeting();
|
||||
}
|
||||
|
||||
public static class FriendlyGreetings
|
||||
{
|
||||
@Produces
|
||||
@Named("friendly")
|
||||
public Greetings friendly(InjectionPoint ip)
|
||||
{
|
||||
return () -> "Hello " + ip.getMember().getDeclaringClass().getSimpleName();
|
||||
}
|
||||
|
||||
@Produces
|
||||
@Named("old")
|
||||
public Greetings old()
|
||||
{
|
||||
return () -> "Salutations!";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="
|
||||
http://xmlns.jcp.org/xml/ns/javaee
|
||||
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd" bean-discovery-mode="all">
|
||||
</beans>
|
|
@ -32,6 +32,7 @@ import org.eclipse.jetty.util.Pool;
|
|||
import org.eclipse.jetty.util.Promise;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
import org.eclipse.jetty.util.thread.Sweeper;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -40,26 +41,32 @@ import org.slf4j.LoggerFactory;
|
|||
import static java.util.stream.Collectors.toCollection;
|
||||
|
||||
@ManagedObject
|
||||
public abstract class AbstractConnectionPool implements ConnectionPool, Dumpable, Sweeper.Sweepable
|
||||
public abstract class AbstractConnectionPool extends ContainerLifeCycle implements ConnectionPool, Dumpable, Sweeper.Sweepable
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AbstractConnectionPool.class);
|
||||
|
||||
private final HttpDestination destination;
|
||||
private final Callback requester;
|
||||
private final Pool<Connection> pool;
|
||||
private boolean maximizeConnections;
|
||||
|
||||
protected AbstractConnectionPool(HttpDestination destination, int maxConnections, boolean cache, Callback requester)
|
||||
{
|
||||
this(destination, new Pool<>(Pool.StrategyType.FIRST, maxConnections, cache), requester);
|
||||
}
|
||||
|
||||
protected AbstractConnectionPool(HttpDestination destination, Pool<Connection> pool, Callback requester)
|
||||
{
|
||||
this.destination = destination;
|
||||
this.requester = requester;
|
||||
@SuppressWarnings("unchecked")
|
||||
Pool<Connection> pool = destination.getBean(Pool.class);
|
||||
if (pool == null)
|
||||
{
|
||||
pool = new Pool<>(maxConnections, cache ? 1 : 0);
|
||||
destination.addBean(pool);
|
||||
}
|
||||
this.pool = pool;
|
||||
addBean(pool);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doStop() throws Exception
|
||||
{
|
||||
pool.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -68,7 +75,7 @@ public abstract class AbstractConnectionPool implements ConnectionPool, Dumpable
|
|||
CompletableFuture<?>[] futures = new CompletableFuture[connectionCount];
|
||||
for (int i = 0; i < connectionCount; i++)
|
||||
{
|
||||
futures[i] = tryCreateReturningFuture(pool.getMaxEntries());
|
||||
futures[i] = tryCreateAsync(getMaxConnectionCount());
|
||||
}
|
||||
return CompletableFuture.allOf(futures);
|
||||
}
|
||||
|
@ -130,16 +137,47 @@ public abstract class AbstractConnectionPool implements ConnectionPool, Dumpable
|
|||
}
|
||||
|
||||
@Override
|
||||
@ManagedAttribute("Whether this pool is closed")
|
||||
public boolean isClosed()
|
||||
{
|
||||
return pool.isClosed();
|
||||
}
|
||||
|
||||
@ManagedAttribute("Whether the pool tries to maximize the number of connections used")
|
||||
public boolean isMaximizeConnections()
|
||||
{
|
||||
return maximizeConnections;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets whether the number of connections should be maximized.</p>
|
||||
*
|
||||
* @param maximizeConnections whether the number of connections should be maximized
|
||||
*/
|
||||
public void setMaximizeConnections(boolean maximizeConnections)
|
||||
{
|
||||
this.maximizeConnections = maximizeConnections;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns an idle connection, if available;
|
||||
* if an idle connection is not available, and the given {@code create} parameter is {@code true}
|
||||
* or {@link #isMaximizeConnections()} is {@code true},
|
||||
* then schedules the opening of a new connection, if possible within the configuration of this
|
||||
* connection pool (for example, if it does not exceed the max connection count);
|
||||
* otherwise returns {@code null}.</p>
|
||||
*
|
||||
* @param create whether to schedule the opening of a connection if no idle connections are available
|
||||
* @return an idle connection or {@code null} if no idle connections are available
|
||||
* @see #tryCreate(int)
|
||||
*/
|
||||
@Override
|
||||
public Connection acquire(boolean create)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Acquiring create={} on {}", create, this);
|
||||
Connection connection = activate();
|
||||
if (connection == null && create)
|
||||
if (connection == null && (create || isMaximizeConnections()))
|
||||
{
|
||||
tryCreate(destination.getQueuedRequestCount());
|
||||
connection = activate();
|
||||
|
@ -159,22 +197,21 @@ public abstract class AbstractConnectionPool implements ConnectionPool, Dumpable
|
|||
*/
|
||||
protected void tryCreate(int maxPending)
|
||||
{
|
||||
tryCreateReturningFuture(maxPending);
|
||||
tryCreateAsync(maxPending);
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> tryCreateReturningFuture(int maxPending)
|
||||
private CompletableFuture<Void> tryCreateAsync(int maxPending)
|
||||
{
|
||||
int connectionCount = getConnectionCount();
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("tryCreate {}/{} connections {}/{} pending", pool.size(), pool.getMaxEntries(), getPendingConnectionCount(), maxPending);
|
||||
}
|
||||
LOG.debug("Try creating connection {}/{} with {}/{} pending", connectionCount, getMaxConnectionCount(), getPendingConnectionCount(), maxPending);
|
||||
|
||||
Pool<Connection>.Entry entry = pool.reserve(maxPending);
|
||||
if (entry == null)
|
||||
return CompletableFuture.completedFuture(null);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("newConnection {}/{} connections {}/{} pending", pool.size(), pool.getMaxEntries(), getPendingConnectionCount(), maxPending);
|
||||
LOG.debug("Creating connection {}/{}", connectionCount, getMaxConnectionCount());
|
||||
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
destination.newConnection(new Promise<>()
|
||||
|
@ -183,7 +220,7 @@ public abstract class AbstractConnectionPool implements ConnectionPool, Dumpable
|
|||
public void succeeded(Connection connection)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Connection {}/{} creation succeeded {}", pool.size(), pool.getMaxEntries(), connection);
|
||||
LOG.debug("Connection {}/{} creation succeeded {}", connectionCount, getMaxConnectionCount(), connection);
|
||||
if (!(connection instanceof Attachable))
|
||||
{
|
||||
failed(new IllegalArgumentException("Invalid connection object: " + connection));
|
||||
|
@ -201,7 +238,7 @@ public abstract class AbstractConnectionPool implements ConnectionPool, Dumpable
|
|||
public void failed(Throwable x)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Connection " + pool.size() + "/" + pool.getMaxEntries() + " creation failed", x);
|
||||
LOG.debug("Connection {}/{} creation failed", connectionCount, getMaxConnectionCount(), x);
|
||||
entry.remove();
|
||||
future.completeExceptionally(x);
|
||||
requester.failed(x);
|
||||
|
@ -240,7 +277,7 @@ public abstract class AbstractConnectionPool implements ConnectionPool, Dumpable
|
|||
if (entry != null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("activated {}", entry);
|
||||
LOG.debug("Activated {} {}", entry, pool);
|
||||
Connection connection = entry.getPooled();
|
||||
acquired(connection);
|
||||
return connection;
|
||||
|
@ -258,8 +295,6 @@ public abstract class AbstractConnectionPool implements ConnectionPool, Dumpable
|
|||
Pool<Connection>.Entry entry = (Pool<Connection>.Entry)attachable.getAttachment();
|
||||
if (entry == null)
|
||||
return false;
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("isActive {}", entry);
|
||||
return !entry.isIdle();
|
||||
}
|
||||
|
||||
|
@ -283,7 +318,7 @@ public abstract class AbstractConnectionPool implements ConnectionPool, Dumpable
|
|||
return true;
|
||||
boolean reusable = pool.release(entry);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Released ({}) {}", reusable, entry);
|
||||
LOG.debug("Released ({}) {} {}", reusable, entry, pool);
|
||||
if (reusable)
|
||||
return true;
|
||||
remove(connection);
|
||||
|
@ -308,7 +343,7 @@ public abstract class AbstractConnectionPool implements ConnectionPool, Dumpable
|
|||
attachable.setAttachment(null);
|
||||
boolean removed = pool.remove(entry);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Removed ({}) {}", removed, entry);
|
||||
LOG.debug("Removed ({}) {} {}", removed, entry, pool);
|
||||
if (removed || force)
|
||||
{
|
||||
released(connection);
|
||||
|
|
|
@ -137,7 +137,7 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
if (result.getResponseFailure() != null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Authentication challenge failed {}", result.getFailure());
|
||||
LOG.debug("Authentication challenge failed", result.getFailure());
|
||||
forwardFailureComplete(request, result.getRequestFailure(), response, result.getResponseFailure());
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
|
||||
package org.eclipse.jetty.client;
|
||||
|
||||
import org.eclipse.jetty.client.api.Connection;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.Pool;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
|
||||
|
@ -27,12 +29,17 @@ public class DuplexConnectionPool extends AbstractConnectionPool
|
|||
{
|
||||
public DuplexConnectionPool(HttpDestination destination, int maxConnections, Callback requester)
|
||||
{
|
||||
this(destination, maxConnections, true, requester);
|
||||
this(destination, maxConnections, false, requester);
|
||||
}
|
||||
|
||||
public DuplexConnectionPool(HttpDestination destination, int maxConnections, boolean cache, Callback requester)
|
||||
{
|
||||
super(destination, maxConnections, cache, requester);
|
||||
this(destination, new Pool<>(Pool.StrategyType.FIRST, maxConnections, cache), requester);
|
||||
}
|
||||
|
||||
public DuplexConnectionPool(HttpDestination destination, Pool<Connection> pool, Callback requester)
|
||||
{
|
||||
super(destination, pool, requester);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -31,7 +31,6 @@ import java.nio.channels.SocketChannel;
|
|||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
@ -563,7 +562,8 @@ public class HttpClient extends ContainerLifeCycle
|
|||
@Override
|
||||
public void succeeded(List<InetSocketAddress> socketAddresses)
|
||||
{
|
||||
Map<String, Object> context = new HashMap<>();
|
||||
// Multiple threads may access the map, especially with DEBUG logging enabled.
|
||||
Map<String, Object> context = new ConcurrentHashMap<>();
|
||||
context.put(ClientConnectionFactory.CLIENT_CONTEXT_KEY, HttpClient.this);
|
||||
context.put(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY, destination);
|
||||
connect(socketAddresses, 0, context);
|
||||
|
|
|
@ -370,7 +370,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
|
|||
{
|
||||
// Aggressively send other queued requests
|
||||
// in case connections are multiplexed.
|
||||
return getHttpExchanges().size() > 0 ? ProcessResult.CONTINUE : ProcessResult.FINISH;
|
||||
return getQueuedRequestCount() > 0 ? ProcessResult.CONTINUE : ProcessResult.FINISH;
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
|
@ -427,11 +427,16 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
|
|||
{
|
||||
if (connectionPool.isActive(connection))
|
||||
{
|
||||
// trigger the next request after releasing the connection
|
||||
// Trigger the next request after releasing the connection.
|
||||
if (connectionPool.release(connection))
|
||||
{
|
||||
send(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
connection.close();
|
||||
send(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -496,7 +496,7 @@ public abstract class HttpReceiver
|
|||
return false;
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Response failure " + exchange.getResponse(), failure);
|
||||
LOG.debug("Response failure {}", exchange.getResponse(), failure);
|
||||
|
||||
// Mark atomically the response as completed, with respect
|
||||
// to concurrency between response success and response failure.
|
||||
|
|
|
@ -226,7 +226,7 @@ public abstract class HttpSender
|
|||
return;
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Request failure " + exchange.getRequest(), failure);
|
||||
LOG.debug("Request failure {}", exchange.getRequest(), failure);
|
||||
|
||||
// Mark atomically the request as completed, with respect
|
||||
// to concurrency between request success and request failure.
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.eclipse.jetty.client;
|
|||
import org.eclipse.jetty.client.api.Connection;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.LeakDetector;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -40,40 +41,16 @@ public class LeakTrackingConnectionPool extends DuplexConnectionPool
|
|||
public LeakTrackingConnectionPool(HttpDestination destination, int maxConnections, Callback requester)
|
||||
{
|
||||
super((HttpDestination)destination, maxConnections, requester);
|
||||
start();
|
||||
}
|
||||
|
||||
private void start()
|
||||
{
|
||||
try
|
||||
{
|
||||
leakDetector.start();
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
addBean(leakDetector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
stop();
|
||||
LifeCycle.stop(this);
|
||||
super.close();
|
||||
}
|
||||
|
||||
private void stop()
|
||||
{
|
||||
try
|
||||
{
|
||||
leakDetector.stop();
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void acquired(Connection connection)
|
||||
{
|
||||
|
@ -90,6 +67,6 @@ public class LeakTrackingConnectionPool extends DuplexConnectionPool
|
|||
|
||||
protected void leaked(LeakDetector.LeakInfo leakInfo)
|
||||
{
|
||||
LOG.info("Connection " + leakInfo.getResourceDescription() + " leaked at:", leakInfo.getStackFrames());
|
||||
LOG.info("Connection {} leaked at:", leakInfo.getResourceDescription(), leakInfo.getStackFrames());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
|
||||
package org.eclipse.jetty.client;
|
||||
|
||||
import org.eclipse.jetty.client.api.Connection;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.Pool;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
|
||||
|
@ -27,12 +29,17 @@ public class MultiplexConnectionPool extends AbstractConnectionPool implements C
|
|||
{
|
||||
public MultiplexConnectionPool(HttpDestination destination, int maxConnections, Callback requester, int maxMultiplex)
|
||||
{
|
||||
this(destination, maxConnections, true, requester, maxMultiplex);
|
||||
this(destination, maxConnections, false, requester, maxMultiplex);
|
||||
}
|
||||
|
||||
public MultiplexConnectionPool(HttpDestination destination, int maxConnections, boolean cache, Callback requester, int maxMultiplex)
|
||||
{
|
||||
super(destination, maxConnections, cache, requester);
|
||||
this(destination, new Pool<>(Pool.StrategyType.FIRST, maxConnections, cache), requester, maxMultiplex);
|
||||
}
|
||||
|
||||
public MultiplexConnectionPool(HttpDestination destination, Pool<Connection> pool, Callback requester, int maxMultiplex)
|
||||
{
|
||||
super(destination, pool, requester);
|
||||
setMaxMultiplex(maxMultiplex);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.client;
|
||||
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.Pool;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
|
||||
/**
|
||||
* <p>A {@link ConnectionPool} that provides connections
|
||||
* randomly among the ones that are available.</p>
|
||||
*/
|
||||
@ManagedObject
|
||||
public class RandomConnectionPool extends MultiplexConnectionPool
|
||||
{
|
||||
public RandomConnectionPool(HttpDestination destination, int maxConnections, Callback requester, int maxMultiplex)
|
||||
{
|
||||
super(destination, new Pool<>(Pool.StrategyType.RANDOM, maxConnections, false), requester, maxMultiplex);
|
||||
}
|
||||
}
|
|
@ -62,7 +62,7 @@ public class RequestNotifier
|
|||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
LOG.info("Exception while notifying listener {}", listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,7 @@ public class RequestNotifier
|
|||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
LOG.info("Exception while notifying listener {}", listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,7 @@ public class RequestNotifier
|
|||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
LOG.info("Exception while notifying listener {}", listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,7 +152,7 @@ public class RequestNotifier
|
|||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
LOG.info("Exception while notifying listener {}", listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,7 +194,7 @@ public class RequestNotifier
|
|||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
LOG.info("Exception while notifying listener {}", listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,7 +224,7 @@ public class RequestNotifier
|
|||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
LOG.info("Exception while notifying listener {}", listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,7 +254,7 @@ public class RequestNotifier
|
|||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
LOG.info("Exception while notifying listener {}", listener, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ public class ResponseNotifier
|
|||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
LOG.info("Exception while notifying listener {}", listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ public class ResponseNotifier
|
|||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
LOG.info("Exception while notifying listener {}", listener, x);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ public class ResponseNotifier
|
|||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
LOG.info("Exception while notifying listener {}", listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,7 @@ public class ResponseNotifier
|
|||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
LOG.info("Exception while notifying listener {}", listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,7 +156,7 @@ public class ResponseNotifier
|
|||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
LOG.info("Exception while notifying listener {}", listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,7 +177,7 @@ public class ResponseNotifier
|
|||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
LOG.info("Exception while notifying listener {}", listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,7 +198,7 @@ public class ResponseNotifier
|
|||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
LOG.info("Exception while notifying listener {}", listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,7 +219,7 @@ public class ResponseNotifier
|
|||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
LOG.info("Exception while notifying listener " + listener, x);
|
||||
LOG.info("Exception while notifying listener {}", listener, x);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,23 +18,37 @@
|
|||
|
||||
package org.eclipse.jetty.client;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.eclipse.jetty.client.api.Connection;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.Pool;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* <p>A {@link ConnectionPool} that attempts to provide connections using a round-robin algorithm.</p>
|
||||
* <p>The round-robin behavior is almost impossible to achieve for several reasons:</p>
|
||||
* <ul>
|
||||
* <li>the server takes different times to serve different requests; if a request takes a long
|
||||
* time to be processed by the server, it would be a performance penalty to stall sending requests
|
||||
* waiting for that connection to be available - better skip it and try another connection</li>
|
||||
* <li>connections may be closed by the client or by the server, so it would be a performance
|
||||
* penalty to stall sending requests waiting for a new connection to be opened</li>
|
||||
* <li>thread scheduling on both client and server may temporarily penalize a connection</li>
|
||||
* </ul>
|
||||
* <p>Do not expect this class to provide connections in a perfect recurring sequence such as
|
||||
* {@code c0, c1, ..., cN-1, c0, c1, ..., cN-1, c0, c1, ...} because that is impossible to
|
||||
* achieve in a real environment.
|
||||
* This class will just attempt a best-effort to provide the connections in a sequential order,
|
||||
* but most likely the order will be quasi-random.</p>
|
||||
* <p>Applications using this class should {@link #preCreateConnections(int) pre-create}
|
||||
* the connections to ensure that they are already opened when the application starts to requests
|
||||
* them, otherwise the first connection that is opened may be used multiple times before the others
|
||||
* are opened, resulting in a behavior that is more random-like than more round-robin-like (and
|
||||
* that confirms that round-robin behavior is almost impossible to achieve).</p>
|
||||
*
|
||||
* @see RandomConnectionPool
|
||||
*/
|
||||
@ManagedObject
|
||||
public class RoundRobinConnectionPool extends MultiplexConnectionPool
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RoundRobinConnectionPool.class);
|
||||
|
||||
private final AtomicInteger offset = new AtomicInteger();
|
||||
private final Pool<Connection> pool;
|
||||
|
||||
public RoundRobinConnectionPool(HttpDestination destination, int maxConnections, Callback requester)
|
||||
{
|
||||
this(destination, maxConnections, requester, 1);
|
||||
|
@ -42,31 +56,11 @@ public class RoundRobinConnectionPool extends MultiplexConnectionPool
|
|||
|
||||
public RoundRobinConnectionPool(HttpDestination destination, int maxConnections, Callback requester, int maxMultiplex)
|
||||
{
|
||||
super(destination, maxConnections, false, requester, maxMultiplex);
|
||||
pool = destination.getBean(Pool.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Connection activate()
|
||||
{
|
||||
int offset = this.offset.get();
|
||||
Connection connection = activate(offset);
|
||||
if (connection != null)
|
||||
this.offset.getAndIncrement();
|
||||
return connection;
|
||||
}
|
||||
|
||||
private Connection activate(int offset)
|
||||
{
|
||||
Pool<Connection>.Entry entry = pool.acquireAt(Math.abs(offset % pool.getMaxEntries()));
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("activated '{}'", entry);
|
||||
if (entry != null)
|
||||
{
|
||||
Connection connection = entry.getPooled();
|
||||
acquired(connection);
|
||||
return connection;
|
||||
}
|
||||
return null;
|
||||
super(destination, new Pool<>(Pool.StrategyType.ROUND_ROBIN, maxConnections, false), requester, maxMultiplex);
|
||||
// If there are queued requests and connections get
|
||||
// closed due to idle timeout or overuse, we want to
|
||||
// aggressively try to open new connections to replace
|
||||
// those that were closed to process queued requests.
|
||||
setMaximizeConnections(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,8 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
private RetainableByteBuffer networkBuffer;
|
||||
private boolean shutdown;
|
||||
private boolean complete;
|
||||
private boolean unsolicited;
|
||||
private int status;
|
||||
|
||||
public HttpReceiverOverHTTP(HttpChannelOverHTTP channel)
|
||||
{
|
||||
|
@ -131,16 +133,18 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
|
||||
protected ByteBuffer onUpgradeFrom()
|
||||
{
|
||||
ByteBuffer upgradeBuffer = null;
|
||||
if (networkBuffer.hasRemaining())
|
||||
{
|
||||
HttpClient client = getHttpDestination().getHttpClient();
|
||||
ByteBuffer upgradeBuffer = BufferUtil.allocate(networkBuffer.remaining(), client.isUseInputDirectByteBuffers());
|
||||
upgradeBuffer = BufferUtil.allocate(networkBuffer.remaining(), client.isUseInputDirectByteBuffers());
|
||||
BufferUtil.clearToFill(upgradeBuffer);
|
||||
BufferUtil.put(networkBuffer.getBuffer(), upgradeBuffer);
|
||||
BufferUtil.flipToFlush(upgradeBuffer, 0);
|
||||
return upgradeBuffer;
|
||||
}
|
||||
return null;
|
||||
|
||||
releaseNetworkBuffer();
|
||||
return upgradeBuffer;
|
||||
}
|
||||
|
||||
private void process()
|
||||
|
@ -159,12 +163,11 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
return;
|
||||
}
|
||||
|
||||
// Connection may be closed or upgraded in a parser callback.
|
||||
boolean upgraded = connection != endPoint.getConnection();
|
||||
if (connection.isClosed() || upgraded)
|
||||
// Connection may be closed in a parser callback.
|
||||
if (connection.isClosed())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("{} {}", upgraded ? "Upgraded" : "Closed", connection);
|
||||
LOG.debug("Closed {}", connection);
|
||||
releaseNetworkBuffer();
|
||||
return;
|
||||
}
|
||||
|
@ -229,6 +232,14 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Parse complete={}, remaining {} {}", complete, networkBuffer.remaining(), parser);
|
||||
|
||||
if (complete)
|
||||
{
|
||||
int status = this.status;
|
||||
this.status = 0;
|
||||
if (status == HttpStatus.SWITCHING_PROTOCOLS_101)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (networkBuffer.isEmpty())
|
||||
return false;
|
||||
|
||||
|
@ -272,9 +283,11 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
public void startResponse(HttpVersion version, int status, String reason)
|
||||
{
|
||||
HttpExchange exchange = getHttpExchange();
|
||||
unsolicited = exchange == null;
|
||||
if (exchange == null)
|
||||
return;
|
||||
|
||||
this.status = status;
|
||||
String method = exchange.getRequest().getMethod();
|
||||
parser.setHeadResponse(HttpMethod.HEAD.is(method) ||
|
||||
(HttpMethod.CONNECT.is(method) && status == HttpStatus.OK_200));
|
||||
|
@ -287,7 +300,8 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
public void parsedHeader(HttpField field)
|
||||
{
|
||||
HttpExchange exchange = getHttpExchange();
|
||||
if (exchange == null)
|
||||
unsolicited |= exchange == null;
|
||||
if (unsolicited)
|
||||
return;
|
||||
|
||||
responseHeader(exchange, field);
|
||||
|
@ -297,7 +311,8 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
public boolean headerComplete()
|
||||
{
|
||||
HttpExchange exchange = getHttpExchange();
|
||||
if (exchange == null)
|
||||
unsolicited |= exchange == null;
|
||||
if (unsolicited)
|
||||
return false;
|
||||
|
||||
// Store the EndPoint is case of upgrades, tunnels, etc.
|
||||
|
@ -309,7 +324,8 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
public boolean content(ByteBuffer buffer)
|
||||
{
|
||||
HttpExchange exchange = getHttpExchange();
|
||||
if (exchange == null)
|
||||
unsolicited |= exchange == null;
|
||||
if (unsolicited)
|
||||
return false;
|
||||
|
||||
RetainableByteBuffer networkBuffer = this.networkBuffer;
|
||||
|
@ -331,7 +347,8 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
public void parsedTrailer(HttpField trailer)
|
||||
{
|
||||
HttpExchange exchange = getHttpExchange();
|
||||
if (exchange == null)
|
||||
unsolicited |= exchange == null;
|
||||
if (unsolicited)
|
||||
return;
|
||||
|
||||
exchange.getResponse().trailer(trailer);
|
||||
|
@ -341,8 +358,12 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
public boolean messageComplete()
|
||||
{
|
||||
HttpExchange exchange = getHttpExchange();
|
||||
if (exchange == null)
|
||||
if (exchange == null || unsolicited)
|
||||
{
|
||||
// We received an unsolicited response from the server.
|
||||
getHttpConnection().close();
|
||||
return false;
|
||||
}
|
||||
|
||||
int status = exchange.getResponse().getStatus();
|
||||
if (status != HttpStatus.CONTINUE_100)
|
||||
|
@ -359,7 +380,7 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
{
|
||||
HttpExchange exchange = getHttpExchange();
|
||||
HttpConnectionOverHTTP connection = getHttpConnection();
|
||||
if (exchange == null)
|
||||
if (exchange == null || unsolicited)
|
||||
connection.close();
|
||||
else
|
||||
failAndClose(new EOFException(String.valueOf(connection)));
|
||||
|
@ -369,7 +390,11 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
|
|||
public void badMessage(BadMessageException failure)
|
||||
{
|
||||
HttpExchange exchange = getHttpExchange();
|
||||
if (exchange != null)
|
||||
if (exchange == null || unsolicited)
|
||||
{
|
||||
getHttpConnection().close();
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpResponse response = exchange.getResponse();
|
||||
response.status(failure.getCode()).reason(failure.getReason());
|
||||
|
|
|
@ -50,7 +50,6 @@ import org.eclipse.jetty.util.SocketAddressResolver;
|
|||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
|
@ -65,11 +64,17 @@ public class ConnectionPoolTest
|
|||
private HttpClient client;
|
||||
|
||||
public static Stream<ConnectionPoolFactory> pools()
|
||||
{
|
||||
return Stream.concat(poolsNoRoundRobin(),
|
||||
Stream.of(new ConnectionPoolFactory("round-robin", destination -> new RoundRobinConnectionPool(destination, destination.getHttpClient().getMaxConnectionsPerDestination(), destination))));
|
||||
}
|
||||
|
||||
public static Stream<ConnectionPoolFactory> poolsNoRoundRobin()
|
||||
{
|
||||
return Stream.of(
|
||||
new ConnectionPoolFactory("duplex", destination -> new DuplexConnectionPool(destination, destination.getHttpClient().getMaxConnectionsPerDestination(), destination)),
|
||||
new ConnectionPoolFactory("round-robin", destination -> new RoundRobinConnectionPool(destination, destination.getHttpClient().getMaxConnectionsPerDestination(), destination)),
|
||||
new ConnectionPoolFactory("multiplex", destination -> new MultiplexConnectionPool(destination, destination.getHttpClient().getMaxConnectionsPerDestination(), destination, 1))
|
||||
new ConnectionPoolFactory("multiplex", destination -> new MultiplexConnectionPool(destination, destination.getHttpClient().getMaxConnectionsPerDestination(), destination, 1)),
|
||||
new ConnectionPoolFactory("random", destination -> new RandomConnectionPool(destination, destination.getHttpClient().getMaxConnectionsPerDestination(), destination, 1))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -301,11 +306,11 @@ public class ConnectionPoolTest
|
|||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("pools")
|
||||
@MethodSource("poolsNoRoundRobin")
|
||||
public void testConcurrentRequestsDontOpenTooManyConnections(ConnectionPoolFactory factory) throws Exception
|
||||
{
|
||||
// Round robin connection pool does open a few more connections than expected.
|
||||
Assumptions.assumeFalse(factory.name.equals("round-robin"));
|
||||
// Round robin connection pool does open a few more
|
||||
// connections than expected, exclude it from this test.
|
||||
|
||||
startServer(new EmptyServerHandler());
|
||||
|
||||
|
|
|
@ -1820,6 +1820,65 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
|||
assertArrayEquals(bytes, baos.toByteArray());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(ScenarioProvider.class)
|
||||
public void testUnsolicitedResponseBytesFromServer(Scenario scenario) throws Exception
|
||||
{
|
||||
String response = "" +
|
||||
"HTTP/1.1 408 Request Timeout\r\n" +
|
||||
"Content-Length: 0\r\n" +
|
||||
"Connection: close\r\n" +
|
||||
"\r\n";
|
||||
testUnsolicitedBytesFromServer(scenario, response);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ArgumentsSource(ScenarioProvider.class)
|
||||
public void testUnsolicitedInvalidBytesFromServer(Scenario scenario) throws Exception
|
||||
{
|
||||
String response = "ABCDEF";
|
||||
testUnsolicitedBytesFromServer(scenario, response);
|
||||
}
|
||||
|
||||
private void testUnsolicitedBytesFromServer(Scenario scenario, String bytesFromServer) throws Exception
|
||||
{
|
||||
try (ServerSocket server = new ServerSocket(0))
|
||||
{
|
||||
startClient(scenario, clientConnector ->
|
||||
{
|
||||
clientConnector.setSelectors(1);
|
||||
HttpClientTransportOverHTTP transport = new HttpClientTransportOverHTTP(clientConnector);
|
||||
transport.setConnectionPoolFactory(destination ->
|
||||
{
|
||||
ConnectionPool connectionPool = new DuplexConnectionPool(destination, 1, destination);
|
||||
connectionPool.preCreateConnections(1);
|
||||
return connectionPool;
|
||||
});
|
||||
return transport;
|
||||
}, null);
|
||||
|
||||
String host = "localhost";
|
||||
int port = server.getLocalPort();
|
||||
|
||||
// Resolve the destination which will pre-create a connection.
|
||||
HttpDestination destination = client.resolveDestination(new Origin("http", host, port));
|
||||
|
||||
// Accept the connection and send an unsolicited 408.
|
||||
try (Socket socket = server.accept())
|
||||
{
|
||||
OutputStream output = socket.getOutputStream();
|
||||
output.write(bytesFromServer.getBytes(StandardCharsets.UTF_8));
|
||||
output.flush();
|
||||
}
|
||||
|
||||
// Give some time to the client to process the response.
|
||||
Thread.sleep(1000);
|
||||
|
||||
AbstractConnectionPool pool = (AbstractConnectionPool)destination.getConnectionPool();
|
||||
assertEquals(0, pool.getConnectionCount());
|
||||
}
|
||||
}
|
||||
|
||||
private void assertCopyRequest(Request original)
|
||||
{
|
||||
Request copy = client.copyRequest((HttpRequest)original, original.getURI());
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html
|
||||
|
||||
[description]
|
||||
Enables Deployer to apply common configuration to all webapp deployments
|
||||
Enables Deployer to apply common configuration to all webapp deployments.
|
||||
|
||||
[depend]
|
||||
deploy
|
||||
|
|
|
@ -184,7 +184,7 @@ public class AppLifeCycle extends Graph
|
|||
for (Binding binding : getBindings(node))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Calling " + binding.getClass().getName() + " for " + app);
|
||||
LOG.debug("Calling {} for {}", binding.getClass().getName(), app);
|
||||
binding.processBinding(node, app);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,6 @@ import org.slf4j.LoggerFactory;
|
|||
public class DeploymentManager extends ContainerLifeCycle
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DeploymentManager.class);
|
||||
private MultiException onStartupErrors;
|
||||
|
||||
/**
|
||||
* Represents a single tracked app within the deployment manager.
|
||||
|
@ -127,6 +126,7 @@ public class DeploymentManager extends ContainerLifeCycle
|
|||
}
|
||||
|
||||
private final AutoLock _lock = new AutoLock();
|
||||
private MultiException _onStartupErrors;
|
||||
private final List<AppProvider> _providers = new ArrayList<AppProvider>();
|
||||
private final AppLifeCycle _lifecycle = new AppLifeCycle();
|
||||
private final Queue<AppEntry> _apps = new ConcurrentLinkedQueue<AppEntry>();
|
||||
|
@ -251,9 +251,10 @@ public class DeploymentManager extends ContainerLifeCycle
|
|||
startAppProvider(provider);
|
||||
}
|
||||
|
||||
if (onStartupErrors != null)
|
||||
try (AutoLock l = _lock.lock())
|
||||
{
|
||||
onStartupErrors.ifExceptionThrow();
|
||||
if (_onStartupErrors != null)
|
||||
_onStartupErrors.ifExceptionThrow();
|
||||
}
|
||||
|
||||
super.doStart();
|
||||
|
@ -524,7 +525,7 @@ public class DeploymentManager extends ContainerLifeCycle
|
|||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
LOG.warn("Unable to reach node goal: " + nodeName, t);
|
||||
LOG.warn("Unable to reach node goal: {}", nodeName, t);
|
||||
|
||||
// migrate to FAILED node
|
||||
Node failed = _lifecycle.getNodeByName(AppLifeCycle.FAILED);
|
||||
|
@ -546,6 +547,17 @@ public class DeploymentManager extends ContainerLifeCycle
|
|||
}
|
||||
}
|
||||
|
||||
private void addOnStartupError(Throwable cause)
|
||||
{
|
||||
try (AutoLock l = _lock.lock())
|
||||
{
|
||||
if (_onStartupErrors == null)
|
||||
_onStartupErrors = new MultiException();
|
||||
|
||||
_onStartupErrors.add(cause);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move an {@link App} through the {@link AppLifeCycle} to the desired {@link Node}, executing each lifecycle step
|
||||
* in the process to reach the desired state.
|
||||
|
@ -564,16 +576,6 @@ public class DeploymentManager extends ContainerLifeCycle
|
|||
requestAppGoal(appentry, nodeName);
|
||||
}
|
||||
|
||||
private void addOnStartupError(Throwable cause)
|
||||
{
|
||||
try (AutoLock l = _lock.lock())
|
||||
{
|
||||
if (onStartupErrors == null)
|
||||
onStartupErrors = new MultiException();
|
||||
onStartupErrors.add(cause);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a contextAttribute that will be set for every Context deployed by this provider.
|
||||
*
|
||||
|
|
|
@ -81,7 +81,7 @@ public class GlobalWebappConfigBinding implements AppLifeCycle.Binding
|
|||
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Binding: Configuring webapp context with global settings from: " + _jettyXml);
|
||||
LOG.debug("Binding: Configuring webapp context with global settings from: {}", _jettyXml);
|
||||
}
|
||||
|
||||
if (_jettyXml == null)
|
||||
|
@ -111,7 +111,7 @@ public class GlobalWebappConfigBinding implements AppLifeCycle.Binding
|
|||
}
|
||||
else
|
||||
{
|
||||
LOG.info("Binding: Unable to locate global webapp context settings: " + _jettyXml);
|
||||
LOG.info("Binding: Unable to locate global webapp context settings: {}", _jettyXml);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,18 +123,18 @@ public abstract class ScanningAppProvider extends ContainerLifeCycle implements
|
|||
protected void doStart() throws Exception
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug(this.getClass().getSimpleName() + ".doStart()");
|
||||
LOG.debug("{}.doStart()", this.getClass().getSimpleName());
|
||||
if (_monitored.size() == 0)
|
||||
throw new IllegalStateException("No configuration dir specified");
|
||||
|
||||
LOG.info("Deployment monitor " + _monitored + " at interval " + _scanInterval);
|
||||
LOG.info("Deployment monitor {}", _monitored);
|
||||
List<File> files = new ArrayList<>();
|
||||
for (Resource resource : _monitored)
|
||||
{
|
||||
if (resource.exists() && resource.getFile().canRead())
|
||||
files.add(resource.getFile());
|
||||
else
|
||||
LOG.warn("Does not exist: " + resource);
|
||||
LOG.warn("Does not exist: {}", resource);
|
||||
}
|
||||
|
||||
_scanner = new Scanner();
|
||||
|
|
|
@ -101,9 +101,7 @@
|
|||
<!-- handler by context path and virtual host, and the -->
|
||||
<!-- DefaultHandler, which handles any requests not handled by -->
|
||||
<!-- the context handlers. -->
|
||||
<!-- Other handlers may be added to the "Handlers" collection, -->
|
||||
<!-- for example the jetty-requestlog.xml file adds the -->
|
||||
<!-- RequestLogHandler after the default handler -->
|
||||
<!-- Other handlers may be added to the "Handlers" collection. -->
|
||||
<!-- =========================================================== -->
|
||||
<Set name="handler">
|
||||
<New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerList">
|
||||
|
|
|
@ -10,11 +10,14 @@
|
|||
<packaging>pom</packaging>
|
||||
|
||||
<properties>
|
||||
<assembly-directory>${basedir}/target/distribution</assembly-directory>
|
||||
<home-directory>${basedir}/target/home</home-directory>
|
||||
<assembly-directory>${project.build.directory}/distribution</assembly-directory>
|
||||
<temp-home-directory>${project.build.directory}/home</temp-home-directory>
|
||||
<home-directory>${assembly-directory}/jetty-home</home-directory>
|
||||
<base-directory>${assembly-directory}/demo-base</base-directory>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
|
||||
<plugins>
|
||||
<!--
|
||||
Copies the additional distribution content over the unpacked jetty-home
|
||||
|
@ -64,9 +67,9 @@
|
|||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-home</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>zip</type>
|
||||
<type>tar.gz</type>
|
||||
<overWrite>true</overWrite>
|
||||
<outputDirectory>${home-directory}</outputDirectory>
|
||||
<outputDirectory>${temp-home-directory}</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
<excludes>META-INF/**</excludes>
|
||||
|
@ -87,7 +90,7 @@
|
|||
<type>war</type>
|
||||
<overWrite>true</overWrite>
|
||||
<includes>**</includes>
|
||||
<outputDirectory>${assembly-directory}/demo-base/webapps</outputDirectory>
|
||||
<outputDirectory>${base-directory}/webapps</outputDirectory>
|
||||
<destFileName>test.war</destFileName>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
|
@ -97,7 +100,7 @@
|
|||
<type>war</type>
|
||||
<overWrite>true</overWrite>
|
||||
<includes>**</includes>
|
||||
<outputDirectory>${assembly-directory}/demo-base/webapps</outputDirectory>
|
||||
<outputDirectory>${base-directory}/webapps</outputDirectory>
|
||||
<destFileName>test-jaas.war</destFileName>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
|
@ -107,7 +110,7 @@
|
|||
<type>war</type>
|
||||
<overWrite>true</overWrite>
|
||||
<includes>**</includes>
|
||||
<outputDirectory>${assembly-directory}/demo-base/webapps</outputDirectory>
|
||||
<outputDirectory>${base-directory}/webapps</outputDirectory>
|
||||
<destFileName>test-jndi.war</destFileName>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
|
@ -117,7 +120,7 @@
|
|||
<type>war</type>
|
||||
<overWrite>true</overWrite>
|
||||
<includes>**</includes>
|
||||
<outputDirectory>${assembly-directory}/demo-base/webapps</outputDirectory>
|
||||
<outputDirectory>${base-directory}/webapps</outputDirectory>
|
||||
<destFileName>test-spec.war</destFileName>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
|
@ -127,7 +130,7 @@
|
|||
<type>war</type>
|
||||
<overWrite>true</overWrite>
|
||||
<includes>**</includes>
|
||||
<outputDirectory>${assembly-directory}/demo-base/webapps</outputDirectory>
|
||||
<outputDirectory>${base-directory}/webapps</outputDirectory>
|
||||
<destFileName>javadoc-proxy.war</destFileName>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
|
@ -137,7 +140,7 @@
|
|||
<type>war</type>
|
||||
<overWrite>true</overWrite>
|
||||
<includes>**</includes>
|
||||
<outputDirectory>${assembly-directory}/demo-base/webapps</outputDirectory>
|
||||
<outputDirectory>${base-directory}/webapps</outputDirectory>
|
||||
<destFileName>async-rest.war</destFileName>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
|
@ -159,7 +162,7 @@
|
|||
<classifier>config</classifier>
|
||||
<type>jar</type>
|
||||
<overWrite>true</overWrite>
|
||||
<outputDirectory>${assembly-directory}</outputDirectory>
|
||||
<outputDirectory>${base-directory}</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
<excludes>META-INF/**</excludes>
|
||||
|
@ -181,7 +184,7 @@
|
|||
<classifier>config</classifier>
|
||||
<type>jar</type>
|
||||
<overWrite>true</overWrite>
|
||||
<outputDirectory>${assembly-directory}</outputDirectory>
|
||||
<outputDirectory>${base-directory}</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
<excludes>META-INF/**</excludes>
|
||||
|
@ -203,7 +206,7 @@
|
|||
<classifier>config</classifier>
|
||||
<type>jar</type>
|
||||
<overWrite>true</overWrite>
|
||||
<outputDirectory>${assembly-directory}</outputDirectory>
|
||||
<outputDirectory>${base-directory}</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
<excludes>META-INF/**</excludes>
|
||||
|
@ -225,7 +228,7 @@
|
|||
<classifier>config</classifier>
|
||||
<type>jar</type>
|
||||
<overWrite>true</overWrite>
|
||||
<outputDirectory>${assembly-directory}</outputDirectory>
|
||||
<outputDirectory>${base-directory}</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
<excludes>META-INF/**</excludes>
|
||||
|
@ -246,7 +249,7 @@
|
|||
<classifier>html</classifier>
|
||||
<type>zip</type>
|
||||
<overWrite>true</overWrite>
|
||||
<outputDirectory>${assembly-directory}/demo-base/webapps/doc</outputDirectory>
|
||||
<outputDirectory>${base-directory}/webapps/doc</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
|
@ -265,8 +268,15 @@
|
|||
</goals>
|
||||
<configuration>
|
||||
<tasks>
|
||||
<copy todir="${home-directory}">
|
||||
<fileset dir="${temp-home-directory}/jetty-home-${project.version}/" />
|
||||
</copy>
|
||||
<copy todir="${assembly-directory}">
|
||||
<fileset dir="${home-directory}/jetty-home-${project.version}/" />
|
||||
<fileset dir="${home-directory}">
|
||||
<include name="NOTICE.txt" />
|
||||
<include name="LICENSE.txt" />
|
||||
<include name="VERSION.txt" />
|
||||
</fileset>
|
||||
</copy>
|
||||
</tasks>
|
||||
</configuration>
|
||||
|
@ -279,64 +289,16 @@
|
|||
</goals>
|
||||
<configuration>
|
||||
<tasks>
|
||||
<chmod dir="${assembly-directory}/bin" perm="755" includes="**/*.sh" />
|
||||
<chmod dir="${home-directory}/bin" perm="755" includes="**/*.sh" />
|
||||
</tasks>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>removeKeystore</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<tasks>
|
||||
<delete file="${assembly-directory}/etc/keystore.p12" />
|
||||
</tasks>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-remote-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>process</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<resourceBundles>
|
||||
<resourceBundle>org.eclipse.jetty.toolchain:jetty-distribution-remote-resources:1.2</resourceBundle>
|
||||
</resourceBundles>
|
||||
<outputDirectory>${assembly-directory}</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<!--
|
||||
Setup the jetty distribution as a jetty.home/jetty.base combo using start.ini configuration mechanism
|
||||
-->
|
||||
<execution>
|
||||
<id>setup home</id>
|
||||
<phase>package</phase>
|
||||
<configuration>
|
||||
<mainClass>org.eclipse.jetty.start.Main</mainClass>
|
||||
<arguments>
|
||||
<argument>jetty.home=${assembly-directory}</argument>
|
||||
<argument>jetty.base=${assembly-directory}</argument>
|
||||
<argument>--add-to-start=server,deploy,websocket,ext,resources,jsp,jstl,http</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
<goals>
|
||||
<goal>java</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<!--
|
||||
Setup the demo-base using the start.d configuration mechanism
|
||||
-->
|
||||
|
@ -346,10 +308,10 @@
|
|||
<configuration>
|
||||
<mainClass>org.eclipse.jetty.start.Main</mainClass>
|
||||
<arguments>
|
||||
<argument>jetty.home=${assembly-directory}</argument>
|
||||
<argument>jetty.base=${assembly-directory}/demo-base</argument>
|
||||
<argument>jetty.home=${home-directory}</argument>
|
||||
<argument>jetty.base=${base-directory}</argument>
|
||||
<argument>--create-startd</argument>
|
||||
<argument>--add-to-start=server,requestlog,deploy,websocket,ext,resources,client,annotations,jndi,servlets,jsp,jstl,http,https,demo</argument>
|
||||
<argument>--add-module=server,requestlog,deploy,websocket,ext,resources,client,annotations,jndi,servlets,jsp,jstl,http,https,demo</argument>
|
||||
</arguments>
|
||||
</configuration>
|
||||
<goals>
|
||||
|
@ -405,9 +367,10 @@
|
|||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-home</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>pom</type>
|
||||
<type>tar.gz</type>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- demo-base/webapps -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>test-jetty-webapp</artifactId>
|
||||
|
@ -415,20 +378,6 @@
|
|||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.tests</groupId>
|
||||
<artifactId>test-proxy-webapp</artifactId>
|
||||
<type>war</type>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.example-async-rest</groupId>
|
||||
<artifactId>example-async-rest-webapp</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>war</type>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.tests</groupId>
|
||||
<artifactId>test-jaas-webapp</artifactId>
|
||||
|
@ -450,6 +399,21 @@
|
|||
<type>war</type>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.tests</groupId>
|
||||
<artifactId>test-proxy-webapp</artifactId>
|
||||
<type>war</type>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.example-async-rest</groupId>
|
||||
<artifactId>example-async-rest-webapp</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>war</type>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- documentation -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-documentation</artifactId>
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
|
||||
JETTY
|
||||
=====
|
||||
The Jetty project is a 100% Java HTTP Server, HTTP Client
|
||||
and Servlet Container from the eclipse foundation
|
||||
|
||||
http://www.eclipse.org/jetty/
|
||||
|
||||
Jetty is open source and is dual licensed using the Apache 2.0 and
|
||||
Eclipse Public License 1.0. You may choose either license when
|
||||
distributing Jetty.
|
||||
|
||||
|
||||
RUNNING JETTY
|
||||
=============
|
||||
The run directory is either the top-level of a binary release
|
||||
or jetty-distribution/target/distribution directory when built from
|
||||
source.
|
||||
|
||||
To run with the default options:
|
||||
|
||||
$ cd demo-base
|
||||
$ java -jar ../start.jar
|
||||
|
||||
To see the available options and the default arguments
|
||||
provided by the start.ini file:
|
||||
|
||||
$ java -jar /path/to/start.jar --help
|
||||
|
||||
|
||||
Many Jetty features can be enabled by using the --module command
|
||||
For example:
|
||||
|
||||
$ cd mybase
|
||||
$ java -jar /path/to/start.jar --module=https,deploy
|
||||
|
||||
Will enable the https and deploy modules (and their transitive
|
||||
dependencies) temporarily for this specific run of Jetty.
|
||||
|
||||
To see what modules are available
|
||||
|
||||
$ java -jar /path/to/start.jar --list-modules
|
||||
|
||||
|
||||
|
||||
JETTY BASE
|
||||
==========
|
||||
|
||||
The jetty.base property is a property that can be defined on the
|
||||
command line (defaults to what your java 'user.dir' property points to)
|
||||
Jetty's start.jar mechanism will configure your jetty instance from
|
||||
the configuration present in this jetty.base directory.
|
||||
|
||||
Example setup:
|
||||
|
||||
# Create the base directory
|
||||
|
||||
$ mkdir mybase
|
||||
$ cd mybase
|
||||
|
||||
# Initialize the base directory's start.ini and needed directories
|
||||
|
||||
$ java -jar /path/to/jetty-dist/start.jar --add-to-start=http,deploy
|
||||
|
||||
# Run this base directory configuration
|
||||
|
||||
$ java -jar /path/to/jetty-dist/start.jar
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
RUNNING JETTY
|
||||
|
||||
Assuming unix commands the general form of running jetty is:
|
||||
|
||||
$ JETTY_HOME=/path/to/jetty-home
|
||||
$ cd /path/to/jetty-base
|
||||
$ java -jar $JETTY_HOME/start.jar
|
||||
|
||||
To see all the options to the start command:
|
||||
$ java -jar $JETTY_HOME/start.jar --help
|
||||
|
||||
|
||||
RUNNING THE DEMO_BASE
|
||||
|
||||
To see the configuration of the included demo-base
|
||||
$ cd demo-base
|
||||
$ java -jar $JETTY_HOME/start.jar --list-config
|
||||
|
||||
To run the demo (from the demo-base directory):
|
||||
$ java -jar $JETTY_HOME/start.jar
|
||||
|
||||
|
||||
RUNNING A WAR
|
||||
|
||||
This distribution contains a jetty-base directory with a minimal configuration.
|
||||
To enable http and webapp deployment for this base
|
||||
$ JETTY_HOME=$PWD/jetty-home
|
||||
$ cd jetty-base
|
||||
$ java -jar $JETTY_HOME/start.jar --add-to-start=http,deploy
|
||||
$ cp /path/to/mywebapp.war webapps
|
||||
|
||||
To see what other modules can be configured:
|
||||
$ java -jar $JETTY_HOME/start.jar --list-modules
|
||||
|
||||
This war in this base can then be run with
|
||||
$ java -jar $JETTY_HOME/start.jar
|
||||
|
||||
|
||||
CREATING A NEW JETTY BASE
|
||||
|
||||
A new Jetty base can be created anywhere on the file system and with any name:
|
||||
$ mkdir /path/to/my-jetty-base
|
||||
$ cd /path/to/my-jetty-base
|
||||
$ java -jar $JETTY_HOME/start.jar --create-startd --add-to-start=server,http,deploy
|
||||
$ cp /path/to/mywebapp.war webapps
|
||||
|
||||
This base can then be run with
|
||||
|
||||
$ java -jar $JETTY_HOME/start.jar
|
|
@ -1,22 +0,0 @@
|
|||
#===========================================================
|
||||
# Jetty Startup
|
||||
#
|
||||
# Starting Jetty from this {jetty.home} is not recommended.
|
||||
#
|
||||
# A proper {jetty.base} directory should be configured, instead
|
||||
# of making changes to this {jetty.home} directory.
|
||||
#
|
||||
# See documentation about {jetty.base} at
|
||||
# http://www.eclipse.org/jetty/documentation/current/startup.html
|
||||
#
|
||||
# A demo-base directory has been provided as an example of
|
||||
# this sort of setup.
|
||||
#
|
||||
# $ cd demo-base
|
||||
# $ java -jar ../start.jar
|
||||
#
|
||||
#===========================================================
|
||||
|
||||
# To disable the warning message, comment the following line
|
||||
--module=home-base-warning
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
|
||||
This directory is scanned by the WebAppDeployer provider for web
|
||||
applications to deploy using the following conventions:
|
||||
|
||||
+ A directory called example/ will be deployed as a standard web
|
||||
application if it contains a WEB-INF/ subdirectory, otherwise it will be
|
||||
deployed as context of static content. The context path will be /example
|
||||
(eg http://localhost:8080/example/) unless the base name is "root" (not
|
||||
case sensitive), in which case the context path is /. If the directory name
|
||||
ends with ".d" it is ignored (but may still be used by explicit configuration).
|
||||
|
||||
+ A file called example.war will be deployed as a standard web application
|
||||
with the context path /example (eg http://localhost:8080/example/). If the
|
||||
base name is root, then the context path is /. If example.war and example/
|
||||
exist, then only the WAR is deployed (which may use the directory as an
|
||||
unpack location).
|
||||
|
||||
+ An XML file like example.xml will be deployed as a context whose
|
||||
configuration is defined by the XML. The context path must be set by
|
||||
the configuration itself. If example.xml and example.war exist, then
|
||||
only the XML is deployed (which may use the war in its configuration).
|
||||
|
||||
This directory is scanned for additions, removals and updates
|
||||
for hot deployment.
|
||||
|
||||
To configure the deployment mechanism, edit the files:
|
||||
start.d/500-deploy.ini
|
||||
etc/jetty-deploy.ini
|
||||
|
||||
To disable the auto deploy mechanism use the command:
|
||||
|
||||
java -jar start.jar --disable=deploy
|
||||
|
|
@ -68,6 +68,11 @@
|
|||
<artifactId>jetty-servlets</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-util-ajax</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-webapp</artifactId>
|
||||
|
@ -94,6 +99,11 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.memcached</groupId>
|
||||
<artifactId>jetty-memcached-sessions</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -165,9 +175,9 @@
|
|||
<SRCDIR>${basedir}/..</SRCDIR>
|
||||
<GITBROWSEURL>https://github.com/eclipse/jetty.project/tree/master</GITBROWSEURL>
|
||||
<GITDOCURL>https://github.com/eclipse/jetty.project/tree/jetty-10.0.x-doc-refactor/jetty-documentation/src/main/asciidoc</GITDOCURL>
|
||||
<DISTGUIDE>../distribution-guide/index.html</DISTGUIDE>
|
||||
<EMBEDGUIDE>../embedded-guide/index.html</EMBEDGUIDE>
|
||||
<QUICKGUIDE>../quickstart-guide/index.html</QUICKGUIDE>
|
||||
<OPGUIDE>../operations-guide/index.html</OPGUIDE>
|
||||
<PROGGUIDE>../programming-guide/index.html</PROGGUIDE>
|
||||
<GSTARTGUIDE>../gettingstarted-guide/index.html</GSTARTGUIDE>
|
||||
<CONTRIBGUIDE>../contribution-guide/index.html</CONTRIBGUIDE>
|
||||
<MVNCENTRAL>http://central.maven.org/maven2</MVNCENTRAL>
|
||||
<VERSION>${project.version}</VERSION>
|
||||
|
@ -176,7 +186,7 @@
|
|||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>quickstart-guide</id>
|
||||
<id>operations-guide</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>process-asciidoc</goal>
|
||||
|
@ -184,23 +194,9 @@
|
|||
<configuration>
|
||||
<backend>html5</backend>
|
||||
<templateDir>${web.resources.directory}/asciidoc/slim-template</templateDir>
|
||||
<sourceDirectory>${basedir}/src/main/asciidoc/quickstart-guide</sourceDirectory>
|
||||
<sourceDirectory>src/main/asciidoc/operations-guide</sourceDirectory>
|
||||
<sourceDocumentName>index.adoc</sourceDocumentName>
|
||||
<outputDirectory>${project.build.directory}/html/quickstart-guide</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>distribution-guide</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>process-asciidoc</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<backend>html5</backend>
|
||||
<templateDir>${web.resources.directory}/asciidoc/slim-template</templateDir>
|
||||
<sourceDirectory>${basedir}/src/main/asciidoc/distribution-guide</sourceDirectory>
|
||||
<sourceDocumentName>index.adoc</sourceDocumentName>
|
||||
<outputDirectory>${project.build.directory}/html/distribution-guide</outputDirectory>
|
||||
<outputDirectory>${project.build.directory}/html/operations-guide</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
|
@ -219,7 +215,7 @@
|
|||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>embedded-guide</id>
|
||||
<id>programming-guide</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>process-asciidoc</goal>
|
||||
|
@ -227,9 +223,9 @@
|
|||
<configuration>
|
||||
<backend>html5</backend>
|
||||
<templateDir>${web.resources.directory}/asciidoc/slim-template</templateDir>
|
||||
<sourceDirectory>${basedir}/src/main/asciidoc/embedded-guide</sourceDirectory>
|
||||
<sourceDirectory>${basedir}/src/main/asciidoc/programming-guide</sourceDirectory>
|
||||
<sourceDocumentName>index.adoc</sourceDocumentName>
|
||||
<outputDirectory>${project.build.directory}/html/embedded-guide</outputDirectory>
|
||||
<outputDirectory>${project.build.directory}/html/programming-guide</outputDirectory>
|
||||
<sourceHighlighter>coderay</sourceHighlighter>
|
||||
<requires>
|
||||
<require>asciidoctor-diagram</require>
|
||||
|
|
|
@ -163,6 +163,59 @@ Intellectual Property is a hallmark concern of the Eclipse Foundation so you are
|
|||
As much as we would like to accept a tremendous pull request, without the proper chain of events being completed our hands are tied.
|
||||
That being said, the steps are not particularly onerous and we are happy to work with you to get them accomplished.
|
||||
|
||||
==== Logging Conventions
|
||||
|
||||
When deciding when and what to log, bear in mind a few things:
|
||||
|
||||
* never use `LOG.debug` without a preceding `if (LOG.isDebugEnabled())`
|
||||
* we don't want to pollute the log with very long stacktraces unless necessary
|
||||
* we don't want to routinely produce logging events in response to data sent by a user
|
||||
* we should not call more than one LOG method for a single event: otherwise log messages may be interleaved and more confusing
|
||||
* we should never LOG.warn and then throw that exception, as that will result in double handling
|
||||
* we should seldom LOG.debug and then throw as that will make debug verbose and add little information
|
||||
* when interacting with a request, or information received from a client:
|
||||
** no logging unless `isDebugEnabled`, in which case you output at `DEBUG` level eg:
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
catch (Throwable t)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Something happened {} {} {}",x, y, z, t);
|
||||
}
|
||||
----
|
||||
|
||||
* when calling into application code that throws an exception:
|
||||
** use `INFO` level, and use `isDebugEnabled` to cut down on the size of the logging of stack traces:
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
catch (Throwable t)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.info("Something happened {} {} {}", x, y, z, t);
|
||||
else
|
||||
LOG.info("Something happened {} {} {} {}", x, y, z, t.toString());
|
||||
}
|
||||
----
|
||||
|
||||
* when exceptions happen in jetty code:
|
||||
** mostly use `WARN` or `ERROR` level
|
||||
** if the exception is not entirely unexpected, can happen relatively frequently, or can potentially have a very long stack trace and you don't want to clutter up the log, you can use `isDebugEnabled` to cut down on the size of the logging of the stacktrace:
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
catch (Throwable t)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.warn("Something happened {} {} {}", x, y, z, t);
|
||||
else
|
||||
LOG.warn("Something happened {} {} {} {}", x, y, z, t.toString());
|
||||
}
|
||||
----
|
||||
|
||||
____
|
||||
[TIP]
|
||||
Be aware that `LOG.warn("Something happened", t)` is the same as `LOG.warn("Something happened {}", t)`, at least for the default jetty logging.
|
||||
In both cases, the full stacktrace is output. If you only want the log message, you need to do `LOG.warn("Something happened {}", t.toString())`.
|
||||
____
|
||||
|
||||
[[cg-patches]]
|
||||
=== Contributing Patches
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[session-management]]
|
||||
== Session Management
|
||||
|
||||
Sessions are a concept within the Servlet api which allow requests to store and retrieve information across the time a user spends in an application.
|
||||
Choosing the correct session manager implementation is an important consideration for every application as each can fit and perform optimally in different situations.
|
||||
If you need a simple in-memory session manager that can persist to disk then session management using the local file system can be a good place to start.
|
||||
If you need a session manager that can work in a clustered scenario with multiple instances of Jetty, then the JDBC session manager can be an excellent option.
|
||||
Jetty also offers more niche session managers that leverage backends such as MongoDB, Inifinispan, or even Google's Cloud Data Store.
|
||||
|
||||
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[]
|
||||
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[]
|
||||
//include::session-clustering-mongodb.adoc[]
|
||||
//include::session-clustering-infinispan.adoc[]
|
||||
//include::session-clustering-gcloud-datastore.adoc[]
|
|
@ -1,276 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[session-clustering-gcloud-datastore]]
|
||||
=== Session Clustering with Google Cloud Datastore
|
||||
|
||||
Jetty can support session clustering by persisting sessions to https://cloud.google.com/datastore/docs/concepts/overview[Google Cloud Datastore].
|
||||
Each Jetty instance locally caches sessions for which it has received requests, writing any changes to the session through to the Datastore as the request exits the server.
|
||||
Sessions must obey the Serialization contract, and servlets must call the `Session.setAttribute()` method to ensure that changes are persisted.
|
||||
|
||||
The persistent session mechanism works in conjunction with a load balancer that supports stickiness.
|
||||
Stickiness can be based on various data items, such as source IP address or characteristics of the session ID or a load-balancer specific mechanism.
|
||||
For those load balancers that examine the session ID, the Jetty persistent session mechanism appends a node ID to the session ID, which can be used for routing.
|
||||
|
||||
==== Configuration
|
||||
|
||||
There are two components to session management in Jetty: a session ID manager and a session manager.
|
||||
|
||||
- The session ID manager ensures that session IDs are unique across all webapps hosted on a Jetty instance, and thus there can only be one session ID manager per Jetty instance.
|
||||
- The session manager handles the session lifecycle (create/update/invalidate/expire) on behalf of a web application, so there is one session manager per web application instance.
|
||||
|
||||
These managers also cooperate and collaborate with the `org.eclipse.jetty.server.session.SessionHandler` to enable cross-context dispatch.
|
||||
|
||||
==== The gcloud-sessions Module
|
||||
|
||||
When using the jetty distribution, to enable Cloud Datastore session persistence, you will first need to enable the `gcloud-sessions` link:#startup-modules[module] for your link:#creating-jetty-base[base] using the `--add-to-start` argument to the link:#startup-overview[start.jar].
|
||||
|
||||
As part of the module installation, the necessary jars will be dynamically downloaded and installed to your `${jetty.base}/lib/gcloud` directory.
|
||||
If you need to up or downgrade the version of the jars, then you can delete the jars that were automatically installed and replace them.
|
||||
Once you've done that, you will need to prevent jetty's startup checks from detecting the missing jars.
|
||||
To do that, you can use `--skip-file-validation=glcoud-sessions` argument to start.jar on the command line, or place that line inside `${jetty.base}/start.ini` to ensure it is used for every start.
|
||||
|
||||
===== Configuring the GCloudSessionIdManager
|
||||
|
||||
The gcloud-sessions module will have installed file called `${jetty.home}/etc/jetty-gcloud-sessions.xml`.
|
||||
This file configures an instance of the `GCloudSessionIdManager` that will be shared across all webapps deployed on that server. It looks like this:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
include::{SRCDIR}/jetty-gcloud/jetty-gcloud-session-manager/src/main/config/etc/jetty-gcloud-sessions.xml[]
|
||||
----
|
||||
|
||||
You configure it by setting values for properties.
|
||||
The properties will either be inserted as commented out in your `start.ini`, or your `start.d/gcloud-sessions.ini` file, depending on how you enabled the module.
|
||||
|
||||
The only property you always need to set is the name of the node in the cluster:
|
||||
|
||||
jetty.gcloudSession.workerName::
|
||||
The name that uniquely identifies this node in the cluster.
|
||||
This value will also be used by the sticky load balancer to identify the node.
|
||||
Don't forget to change the value of this property on *each* node on which you enable gcloud datastore session clustering.
|
||||
|
||||
|
||||
===== Configuring GCloud Datastore
|
||||
|
||||
Things that you will need:
|
||||
|
||||
- a local installation of the https://cloud.google.com/sdk/[Google Cloud SDK]
|
||||
- a project id referred to below as [YOUR PROJECT ID]
|
||||
- to have https://cloud.google.com/datastore/docs/activate[enabled your project id] to use GCloud Datastore
|
||||
|
||||
====== Using GCloud Datastore from Compute/AppEngine
|
||||
|
||||
If you are running your webapp from within ComputeEngine or AppEngine, you do not need to do anything else in order to configure your GCloud setup. All necessary information will be inferred from the environment by the infrastrcture.
|
||||
|
||||
====== Using GCloud Datastore from an external server
|
||||
|
||||
If you are running your webapp externally to Google infrastructure, you can still interact with the remote GCloud Datastore service.
|
||||
|
||||
Execute the following commands:
|
||||
|
||||
- gcloud config set project [YOUR PROJECT ID].
|
||||
- gcloud auth login
|
||||
|
||||
This will populate your environment with the necessary authentication information to allow you to contact the remote GCloud Datastore instance.
|
||||
|
||||
====== Using GCloud Datastore local development server
|
||||
|
||||
If you would like to locally test your application, you can use the Google Cloud SDK's https://cloud.google.com/datastore/docs/tools/datastore-emulator[GCloud Datastore emulator].
|
||||
|
||||
Follow the instructions on the https://cloud.google.com/datastore/docs/tools/datastore-emulator[GCloud Datastore emulator page] to set up your environment.
|
||||
|
||||
===== Configuring the GCloudSessionManager
|
||||
|
||||
As mentioned elsewhere, there must be one `SessionManager` per context (e.g. webapp).
|
||||
Each SessionManager needs to reference the single `GCloudSessionIdManager`.
|
||||
|
||||
The way you configure a `GCloudSessionManager` depends on whether you're configuring from a context xml file, a `jetty-web.xml` file or code.
|
||||
The basic difference is how you get a reference to the Jetty `org.eclipse.jetty.server.Server` instance.
|
||||
|
||||
From a context xml file, you reference the Server instance as a Ref:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
<!-- Get a reference to the GCloudSessionIdManager -->
|
||||
<Ref id="Server">
|
||||
<Call id="idMgr" name="getSessionIdManager"/>
|
||||
</Ref>
|
||||
|
||||
<!-- Use the GCloudSessionIdManager to set up the GCloudSessionManager -->
|
||||
<Set name="sessionHandler">
|
||||
<New class="org.eclipse.jetty.server.session.SessionHandler">
|
||||
<Arg>
|
||||
<New id="mgr" class="org.eclipse.jetty.gcloud.session.GCloudSessionManager">
|
||||
<Set name="sessionIdManager">
|
||||
<Ref id="idMgr"/>
|
||||
</Set>
|
||||
<Set name="scavengeIntervalSec">600</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</New>
|
||||
</Set>
|
||||
----
|
||||
|
||||
From a `WEB-INF/jetty-web.xml` file, you can reference the Server instance directly:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
<!-- Reference the server directly -->
|
||||
<Get name="server">
|
||||
<Get id="idMgr" name="sessionIdManager"/>
|
||||
</Get>
|
||||
|
||||
<!-- Apply the SessionIdManager to the GCloudSessionManager -->
|
||||
<Set name="sessionHandler">
|
||||
<New class="org.eclipse.jetty.server.session.SessionHandler">
|
||||
<Arg>
|
||||
<New id="mgr" class="org.eclipse.jetty.gcloud.session.GCloudSessionManager">
|
||||
<Set name="sessionIdManager">
|
||||
<Ref id="idMgr"/>
|
||||
</Set>
|
||||
<Set name="scavengeIntervalSec">600</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</New>
|
||||
</Set>
|
||||
----
|
||||
|
||||
The `GCloudSessionManager` supports the following configuration setters:
|
||||
|
||||
scavengeIntervalSec::
|
||||
Time in seconds between runs of a scavenger task that looks for expired old sessions to delete.
|
||||
The default is 10 minutes.
|
||||
If set to 0, no scavenging is done.
|
||||
staleIntervalSec::
|
||||
The length of time a session can be in memory without being checked against the cluster.
|
||||
A value of 0 indicates that the session is never checked against the cluster - the current node is considered to be the master for the session.
|
||||
maxQueryResults::
|
||||
The maximum number of results to return for a query to find expired sessions.
|
||||
For efficiency it is important to limit the size of the result.
|
||||
The default is 100.
|
||||
If 0 or negative numbers are set, the default is used instead.
|
||||
|
||||
===== The gcloud-memcached-sessions module
|
||||
|
||||
As an optimization, you can have Jetty store your session data into GCloud Datastore but also cache it into memcached. This serves two purposes: faster read-accesses and also better support for non-sticky load balancers (although using a non-sticky load balancer is highly undesirable and not recommended).
|
||||
|
||||
You will need to enable the `gcloud-memcached-sessions` link:#startup-modules[module] for your link:#creating-jetty-base[base] using the `--add-to-start` argument to the link:#startup-overview[start.jar].
|
||||
|
||||
If you already enabled the gcloud-sessions module, that's fine as the gcloud-memcached-sessions module depends on it anyway.
|
||||
|
||||
Jetty uses the https://github.com/killme2008/xmemcached[Xmemcached] java client.
|
||||
It depends on http://www.slf4j.org/[slf4j], so you will need to choose an http://www.slf4j.org/[slf4j logging implementation]. You can copy the chosen implementation jars into your $jetty.base/lib/ext directory.
|
||||
|
||||
====== Configuring the GCloudSessionIdManager
|
||||
|
||||
The instructions here are exactly the same as for the gcloud-sessions module.
|
||||
|
||||
====== Configuring GCloud Datastore
|
||||
|
||||
The instructions here are exactly the same as for the gcloud-sessions module.
|
||||
|
||||
====== Configuring Memcached
|
||||
|
||||
If you have installed memcached on a host and port other than the defaults of `localhost` and `11211`, then you will need to take note of these values and supply them to the configuration of the `GCloudMemcachedSessionManager`.
|
||||
|
||||
====== Configuring the GCloudMemcachedSessionManager
|
||||
|
||||
*Note that* you will be configuring a `GCloudMemcachedSessionManager` 'instead of' a `GCloudSessionManager`.
|
||||
|
||||
As usual, there must be only one per context (e.g. webapp).
|
||||
Each GCloudMemcachedSessionManager needs to reference the single `GCloudSessionIdManager`.
|
||||
|
||||
|
||||
The way you configure a `GCloudMemcachedSessionManager` depends on whether you're configuring from a context xml file, a `jetty-web.xml` file or code.
|
||||
The basic difference is how you get a reference to the Jetty `org.eclipse.jetty.server.Server` instance.
|
||||
|
||||
From a context xml file, you reference the Server instance as a Ref:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
<!-- Get a reference to the GCloudSessionIdManager -->
|
||||
<Ref id="Server">
|
||||
<Call id="idMgr" name="getSessionIdManager"/>
|
||||
</Ref>
|
||||
|
||||
<!-- Use the GCloudSessionIdManager to set up the GCloudMemcachedSessionManager -->
|
||||
<Set name="sessionHandler">
|
||||
<New class="org.eclipse.jetty.server.session.SessionHandler">
|
||||
<Arg>
|
||||
<New id="mgr" class="org.eclipse.jetty.gcloud.memcached.session.GCloudMemcachedSessionManager">
|
||||
<Set name="sessionIdManager">
|
||||
<Ref id="idMgr"/>
|
||||
</Set>
|
||||
<Set name="scavengeIntervalSec">600</Set>
|
||||
<Set name="host">myhost</Set>
|
||||
<Set name="port">11211</Set>
|
||||
<Set name="expirySec">0</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</New>
|
||||
</Set>
|
||||
----
|
||||
|
||||
From a `WEB-INF/jetty-web.xml` file, you can reference the Server instance directly:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
<!-- Reference the server directly -->
|
||||
<Get name="server">
|
||||
<Get id="idMgr" name="sessionIdManager"/>
|
||||
</Get>
|
||||
|
||||
<!-- Apply the SessionIdManager to the GCloudMemcachedSessionManager -->
|
||||
<Set name="sessionHandler">
|
||||
<New class="org.eclipse.jetty.server.session.SessionHandler">
|
||||
<Arg>
|
||||
<New id="mgr" class="org.eclipse.jetty.gcloud..memcached.session.GCloudMemcachedSessionManager">
|
||||
<Set name="sessionIdManager">
|
||||
<Ref id="idMgr"/>
|
||||
</Set>
|
||||
<Set name="scavengeIntervalSec">600</Set>
|
||||
<Set name="host">myhost</Set>
|
||||
<Set name="port">11211</Set>
|
||||
<Set name="expirySec">0</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</New>
|
||||
</Set>
|
||||
----
|
||||
|
||||
The `GCloudMemcachedSessionManager` supports the following configuration setters:
|
||||
|
||||
scavengeIntervalSec::
|
||||
Time in seconds between runs of a scavenger task that looks for expired old sessions to delete.
|
||||
The default is 10 minutes.
|
||||
If set to 0, no scavenging is done.
|
||||
staleIntervalSec::
|
||||
The length of time a session can be in memory without being checked against the cluster.
|
||||
A value of 0 indicates that the session is never checked against the cluster - the current node is considered to be the master for the session.
|
||||
maxQueryResults::
|
||||
The maximum number of results to return for a query to find expired sessions.
|
||||
For efficiency it is important to limit the size of the result.
|
||||
The default is 100.
|
||||
If 0 or negative numbers are set, the default is used instead.
|
||||
host::
|
||||
The address of the host where the memcached server is running. Defaults to "localhost".
|
||||
port::
|
||||
The port on the host where the memcached serer is running. Defaults to "11211".
|
||||
expirySec::
|
||||
The time in seconds that an entry in the memcached cache is considered valid. By default, entries are are not aged out of the cached, however they may be evicted due to memory constraints.
|
|
@ -1,192 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[session-clustering-infinispan]]
|
||||
=== Session Clustering with Infinispan
|
||||
|
||||
Jetty can support session clustering by persisting sessions to http://www.infinispan.org[Infinispan].
|
||||
Each Jetty instance locally caches sessions for which it has received requests, writing any changes to the session through to Infinispan as the request exits the server.
|
||||
Sessions must obey the Serialization contract, and servlets must call the `Session.setAttribute()` method to ensure that changes are persisted.
|
||||
|
||||
The persistent session mechanism works in conjunction with a load balancer that supports stickiness.
|
||||
Stickiness can be based on various data items, such as source IP address or characteristics of the session ID or a load-balancer specific mechanism.
|
||||
For those load balancers that examine the session ID, the Jetty persistent session mechanism appends a node ID to the session ID, which can be used for routing.
|
||||
|
||||
==== Configuration
|
||||
|
||||
There are two components to session management in Jetty: a session ID manager and a session manager.
|
||||
|
||||
* The session ID manager ensures that session IDs are unique across all webapps hosted on a Jetty instance, and thus there can only be one session ID manager per Jetty instance.
|
||||
* The session manager handles the session lifecycle (create/update/invalidate/expire) on behalf of a web application, so there is one session manager per web application instance.
|
||||
|
||||
These managers also cooperate and collaborate with the `org.eclipse.jetty.server.session.SessionHandler` to enable cross-context dispatch.
|
||||
|
||||
==== The Infinispan Module
|
||||
|
||||
When using the jetty distribution, to enable Infinispan session persistence, you will first need to enable the Infinispan link:#startup-modules[module] for your link:#creating-jetty-base[base] using the `--add-to-start` argument to the link:#startup-overview[start.jar].
|
||||
|
||||
As part of the module installation, the necessary Infinispan jars will be dynamically downloaded and installed to your `${jetty.base}/lib/infinispan` directory.
|
||||
If you need to up or downgrade the version of the Infinispan jars, then you can delete the jars that were automatically installed and replace them.
|
||||
Once you've done that, you will need to prevent Jetty's startup checks from detecting the missing jars.
|
||||
To do that, you can use `--skip-file-validation=infinispan` argument to start.jar on the command line, or place that line inside `${jetty.base}/start.ini` to ensure it is used for every start.
|
||||
|
||||
You will also find the following properties, either in your base's `start.d/infinispan.ini` file or appended to your `start.ini`, depending on how you enabled the module:
|
||||
|
||||
....
|
||||
## Unique identifier for this node in the cluster
|
||||
jetty.infinispanSession.workerName=node1
|
||||
....
|
||||
|
||||
jetty.infinispanSession.workerName::
|
||||
The name that uniquely identifies this node in the cluster.
|
||||
This value will also be used by the sticky load balancer to identify the node.
|
||||
Don't forget to change the value of this property on *each* node on which you enable Infinispan session clustering.
|
||||
|
||||
These properties are applied to the `InfinispanSessionIdManager` described below.
|
||||
|
||||
===== Configuring the InfinispanSessionIdManager
|
||||
|
||||
The Infinispan module will have installed file called `$\{jetty.home}/etc/jetty-infinispan.xml`.
|
||||
This file configures an instance of the `InfinispanSessionIdManager` that will be shared across all webapps deployed on that server.
|
||||
It looks like this:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
include::{SRCDIR}/jetty-infinispan/src/main/config/etc/jetty-infinispan.xml[]
|
||||
----
|
||||
|
||||
As you can see, you configure the Infinispan http://infinispan.org/docs/7.1.x/user_guide/user_guide.html#_the_cache_apis[Cache] instance that the `InfinispanSessionIdManager` should use in this file.
|
||||
By default, the Infinispan http://infinispan.org/docs/7.1.x/getting_started/getting_started.html#_running_infinispan_on_a_single_node[Default cache] instance is used (e.g. on the local node).
|
||||
You can instead use a custom Cache setup - the `jetty-infinispan.xml` file shows you how to configure a remote Cache (using the http://infinispan.org/docs/7.1.x/user_guide/user_guide.html#_using_hot_rod_server[hotrod java client]).
|
||||
|
||||
The `InfinispanSessionIdManager` can be configured by calling setters:
|
||||
|
||||
idleExpiryMultiple::
|
||||
Sessions that are not immortal, e.g. they have an expiry time, have their ids stored into Infinispan with an http://infinispan.org/docs/7.1.x/user_guide/user_guide.html#_expiration[idle expiry timeout] equivalent to double the session's timeout.
|
||||
This should be sufficient to ensure that a session id that is in-use by a session is never accidentally removed.
|
||||
However, should you wish to, you can configure this to any integral value to effectively increase the http://infinispan.org/docs/7.1.x/user_guide/user_guide.html#_expiration[idle expiry] timeout.
|
||||
|
||||
===== Configuring the InfinispanSessionManager
|
||||
|
||||
As mentioned elsewhere, there should be one `InfinispanSessionManager` per context (e.g. webapp).
|
||||
It will need to reference the single `InfinispanSessionIdManager` configured previously for the Server.
|
||||
|
||||
The way you configure a `InfinispanSessionManager` depends on whether you're configuring from a context xml file, a `jetty-web.xml` file or code.
|
||||
The basic difference is how you get a reference to the Jetty `org.eclipse.jetty.server.Server` instance.
|
||||
|
||||
From a context xml file, you reference the Server instance as a Ref:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
<!-- Expose the jetty infinispan classes for session serialization -->
|
||||
<Get name="serverClasspathPattern">
|
||||
<Call name="add">
|
||||
<Arg>-org.eclipse.jetty.session.infinispan.</Arg>
|
||||
</Call>
|
||||
</Get>
|
||||
|
||||
|
||||
<!-- Get a reference to the InfinispanSessionIdManager -->
|
||||
<Ref id="Server">
|
||||
<Call id="idMgr" name="getSessionIdManager"/>
|
||||
</Ref>
|
||||
|
||||
<!-- Get a referencee to the Cache from the InfinispanSessionIdManager -->
|
||||
<Ref id="idMgr">
|
||||
<Get id="cache" name="cache"/>
|
||||
</Ref>
|
||||
|
||||
<!-- Use the InfinispanSessionIdManager and Cache to setup up the InfinispanSessionManager -->
|
||||
<Set name="sessionHandler">
|
||||
<New class="org.eclipse.jetty.server.session.SessionHandler">
|
||||
<Arg>
|
||||
<New id="mgr" class="org.eclipse.jetty.session.infinispan.InfinispanSessionManager">
|
||||
<Set name="sessionIdManager">
|
||||
<Ref id="idMgr"/>
|
||||
</Set>
|
||||
<Set name="cache">
|
||||
<Ref id="cache">
|
||||
</Ref>
|
||||
</Set>
|
||||
<Set name="scavengeInterval">60</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</New>
|
||||
</Set>
|
||||
----
|
||||
|
||||
From a `WEB-INF/jetty-web.xml` file, you can reference the Server instance directly:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
<!-- Expose the jetty infinispan classes for session serialization -->
|
||||
<Get name="serverClasspathPattern">
|
||||
<Call name="add">
|
||||
<Arg>-org.eclipse.jetty.session.infinispan.</Arg>
|
||||
</Call>
|
||||
</Get>
|
||||
|
||||
<!-- Reference the server directly -->
|
||||
<Get name="server">
|
||||
<Get id="idMgr" name="sessionIdManager"/>
|
||||
</Get>
|
||||
|
||||
<!-- Get a reference to the Cache via the InfinispanSessionIdManager -->
|
||||
<Ref id="idMgr">
|
||||
<Get id="cache" name="cache"/>
|
||||
</Ref>
|
||||
|
||||
<!-- Apply the SessionIdManager and Cache to the InfinispanSessionManager -->
|
||||
<Set name="sessionHandler">
|
||||
<New class="org.eclipse.jetty.server.session.SessionHandler">
|
||||
<Arg>
|
||||
<New id="mgr" class="org.eclipse.jetty.session.infinispan.InfinispanSessionManager">
|
||||
<Set name="sessionIdManager">
|
||||
<Ref id="idMgr"/>
|
||||
</Set>
|
||||
<Set name="cache">
|
||||
<Ref id="cache">
|
||||
</Ref>
|
||||
</Set>
|
||||
<Set name="scavengeInterval">600</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</New>
|
||||
</Set>
|
||||
----
|
||||
|
||||
The InfinispanSessionManager can be provided by calling setters:
|
||||
|
||||
scavengeInterval::
|
||||
Time in seconds between runs of a scavenger task that looks for expired old sessions to delete.
|
||||
The default is 10 minutes.
|
||||
staleIntervalSec::
|
||||
The length of time a session can be in memory without being checked against the cluster.
|
||||
A value of 0 indicates that the session is never checked against the cluster - the current node is considered to be the master for the session.
|
||||
|
||||
===== Using HotRod
|
||||
|
||||
If you're using the hotrod client - where serialization will be required - you will need to ensure that the hotrod marshalling software works with Jetty classloading.
|
||||
To do this, firstly ensure that you have included the lines containing the `getServerClasspathPattern().add(...)` to your context xml file as shown above.
|
||||
|
||||
Then, create the file `${jetty.base}/resources/hotrod-client.properties`.
|
||||
Add the following line to this file:
|
||||
|
||||
....
|
||||
infinispan.client.hotrod.marshaller=org.eclipse.jetty.session.infinispan.WebAppMarshaller
|
||||
....
|
|
@ -1,228 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[session-clustering-jdbc]]
|
||||
=== Session Clustering with a Database
|
||||
|
||||
Jetty can support session clustering by persisting sessions to a shared database.
|
||||
Each Jetty instance locally caches sessions for which it has received requests, writing any changes to the session through to the database as the request exits the server.
|
||||
Sessions must obey the Serialization contract, and servlets must call the `Session.setAttribute()` method to ensure that changes are persisted.
|
||||
|
||||
The persistent session mechanism works in conjunction with a load balancer that supports stickiness.
|
||||
Stickiness can be based on various data items, such as source IP address or characteristics of the session ID or a load-balancer specific mechanism.
|
||||
For those load balancers that examine the session ID, the Jetty persistent session mechanism appends a node ID to the session ID, which can be used for routing.
|
||||
|
||||
In this type of solution, the database can become both a bottleneck and a single point of failure.
|
||||
Jetty takes steps to reduce the load on the database (discussed below), but in a heavily loaded environment you might need to investigate other optimization strategies such as local caching and database replication.
|
||||
You should also consult your database vendor's documentation for information on how to ensure high availability and failover of your database.
|
||||
|
||||
==== Configuration
|
||||
|
||||
There are two components to session management in Jetty: a session ID manager and a session manager.
|
||||
|
||||
* The session ID manager ensures that session IDs are unique across all webapps hosted on a Jetty instance, and thus there can only be one session ID manager per Jetty instance.
|
||||
* The session manager handles the session lifecycle (create/update/invalidate/expire) on behalf of a web application, so there is one session manager per web application instance.
|
||||
|
||||
These managers also cooperate and collaborate with the `org.eclipse.jetty.server.session.SessionHandler` to enable cross-context dispatch.
|
||||
|
||||
==== The jdbc-session Module
|
||||
|
||||
When using the jetty distribution, to enable jdbc session persistence, you will first need to enable the jdbc-session link:#startup-modules[module] for your link:#creating-jetty-base[base] using the `--add-to-start` argument to the link:#startup-overview[start.jar].
|
||||
|
||||
You will also find the following properties, either in your base's start.d/jdbc-session.ini file or appended to your start.ini, depending on how you enabled the module:
|
||||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
## Unique identifier for this node in the cluster
|
||||
jetty.jdbcSession.workerName=node1
|
||||
|
||||
##Uncomment either the datasource name or driverClass and connectionURL
|
||||
#jetty.jdbcSession.datasource=sessions
|
||||
jetty.jdbcSession.driverClass=org.apache.derby.jdbc.EmbeddedDriver
|
||||
jetty.jdbcSession.connectionURL=jdbc:derby:sessions;create=true
|
||||
----
|
||||
|
||||
jetty.jdbcSession.workerName::
|
||||
The name that uniquely identifies this node in the cluster.
|
||||
This value will also be used by the sticky load balancer to identify the node.
|
||||
Don't forget to change the value of this property on *each* node on which you enable jdbc session clustering.
|
||||
jetty.jdbcSession.scavenge::
|
||||
The time in seconds between sweeps of a task which scavenges old expired sessions.
|
||||
The default is 10 minutess.
|
||||
Increasing the frequency is not recommended as doing so increases the load on the database with very little gain.
|
||||
jetty.jdbcSession.datasource::
|
||||
The name of a `javax.sql.DataSource` that gives access to the database that holds the session information.
|
||||
You should configure *either* this or the jdbc driver information described next.
|
||||
jetty.jdbcSession.datasource and jetty.jdbcSession.connectionURL::
|
||||
This is the name of the jdbc driver class, and a jdbc connection url suitable for that driver.
|
||||
You should configure *either* this or the jdbc datasource name described above.
|
||||
|
||||
These properties are applied to the `JDBCSessionIdManager` described below.
|
||||
|
||||
===== Configuring the JDBCSessionIdManager
|
||||
|
||||
The jdbc-session module will have installed file called `$\{jetty.home}/etc/jetty-jdbc-sessions.xml`.
|
||||
This file configures an instance of the `JDBCSessionIdManager` that will be shared across all webapps deployed on that server.
|
||||
It looks like this:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
include::{SRCDIR}/jetty-server/src/main/config/etc/jetty-jdbc-sessions.xml[]
|
||||
----
|
||||
|
||||
As well as uncommenting and setting up appropriate values for the properties discussed above, you will also need to edit this file and uncomment *either* the data source or the driver info elements.
|
||||
|
||||
As Jetty configuration files are direct mappings of XML to Java, it is straight forward to do this in code:
|
||||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
Server server = new Server();
|
||||
...
|
||||
JDBCSessionIdManager idMgr = new JDBCSessionIdManager(server);
|
||||
idMgr.setWorkerName("node1");
|
||||
idMgr.setDriverInfo("com.mysql.jdbc.Driver", "jdbc:mysql://127.0.0.1:3306/sessions?user=janb");
|
||||
idMgr.setScavengeInterval(600);
|
||||
server.setSessionIdManager(idMgr);
|
||||
|
||||
----
|
||||
|
||||
====== Configuring the Database Schema
|
||||
|
||||
You may find it necessary to change the names of the tables and columns that the JDBC Session management uses to store the session information.
|
||||
The defaults used are:
|
||||
|
||||
.Default Values for Session Id Table
|
||||
[options="header"]
|
||||
|===========================
|
||||
|table name |JettySessionIds
|
||||
|columns |id
|
||||
|===========================
|
||||
|
||||
.Default Values for Session Table
|
||||
[options="header"]
|
||||
|=======================================================================
|
||||
|table name |JettySessions
|
||||
|
||||
|columns |rowId, sessionId, contextPath, virtualHost, lastNode,
|
||||
accessTime, lastAccessTime, createTime, cookieTime, lastSavedTime,
|
||||
expiryTime, maxInterval, map
|
||||
|=======================================================================
|
||||
|
||||
To change these values, use the link:{JDURL}/org/eclipse/jetty/server/session/SessionIdTableSchema.html[org.eclipse.jetty.server.session.SessionIdTableSchema] and link:{JDURL}/org/eclipse/jetty/server/session/SessionTableSchema.html[org.eclipse.jetty.server.session.SessionTableSchema] classes.
|
||||
These classes have getter/setter methods for the table name and all columns.
|
||||
|
||||
Here's an example of changing the name of `JettySessionsId` table and its single column.
|
||||
This example will use java code, but as explained above, you may also do this via a Jetty xml configuration file:
|
||||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
JDBCSessionIdManager idManager = new JDBCSessionIdManager(server);
|
||||
|
||||
SessionIdTableSchema idTableSchema = new SessionIdTableSchema();
|
||||
idTableSchema.setTableName("mysessionids");
|
||||
idTableSchema.setIdColumn("theid");
|
||||
idManager.setSessionIdTableSchema(idTableSchema);
|
||||
----
|
||||
|
||||
In a similar fashion, you can change the names of the table and columns for the `JettySessions` table.
|
||||
*Note* that both the `SessionIdTableSchema` and the `SessionTableSchema` instances are set on the `JDBCSessionIdManager` class.
|
||||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
JDBCSessionIdManager idManager = new JDBCSessionIdManager(server);
|
||||
|
||||
SessionTableSchema sessionTableSchema = new SessionTableSchema();
|
||||
sessionTableSchema.setTableName("mysessions");
|
||||
sessionTableSchema.setIdColumn("mysessionid");
|
||||
sessionTableSchema.setAccessTimeColumn("atime");
|
||||
sessionTableSchema.setContextPathColumn("cpath");
|
||||
sessionTableSchema.setCookieTimeColumn("cooktime");
|
||||
sessionTableSchema.setCreateTimeColumn("ctime");
|
||||
sessionTableSchema.setExpiryTimeColumn("extime");
|
||||
sessionTableSchema.setLastAccessTimeColumn("latime");
|
||||
sessionTableSchema.setLastNodeColumn("lnode");
|
||||
sessionTableSchema.setLastSavedTimeColumn("lstime");
|
||||
sessionTableSchema.setMapColumn("mo");
|
||||
sessionTableSchema.setMaxIntervalColumn("mi");
|
||||
idManager.setSessionTableSchema(sessionTableSchema);
|
||||
----
|
||||
|
||||
===== Configuring the JDBCSessionManager
|
||||
|
||||
As mentioned elsewhere, there should be one `JDBCSessionManager` per context (e.g. webapp).
|
||||
It will need to reference the single `JDBCSessionIdManager` configured previously for the Server.
|
||||
|
||||
The way you configure a `JDBCSessionManager` depends on whether you're configuring from a context xml file, a `jetty-web.xml` file or code.
|
||||
The basic difference is how you get a reference to the Jetty `org.eclipse.jetty.server.Server` instance.
|
||||
|
||||
From a context xml file, you reference the Server instance as a Ref:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
<Ref id="Server">
|
||||
<Call id="idMgr" name="getSessionIdManager"/>
|
||||
</Ref>
|
||||
<Set name="sessionHandler">
|
||||
<New class="org.eclipse.jetty.server.session.SessionHandler">
|
||||
<Arg>
|
||||
<New id="jdbcmgr" class="org.eclipse.jetty.server.session.JDBCSessionManager">
|
||||
<Set name="sessionIdManager">
|
||||
<Ref id="idMgr"/>
|
||||
</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</New>
|
||||
</Set>
|
||||
----
|
||||
|
||||
From a `WEB-INF/jetty-web.xml` file, you can reference the Server instance directly:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
|
||||
<Get name="server">
|
||||
<Get id="idMgr" name="sessionIdManager"/>
|
||||
</Get>
|
||||
<Set name="sessionHandler">
|
||||
<New class="org.eclipse.jetty.server.session.SessionHandler">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.server.session.JDBCSessionManager">
|
||||
<Set name="sessionIdManager">
|
||||
<Ref id="idMgr"/>
|
||||
</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</New>
|
||||
</Set>
|
||||
----
|
||||
|
||||
If you're embedding this in code:
|
||||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
|
||||
//assuming you have already set up the JDBCSessionIdManager as shown earlier
|
||||
//and have a reference to the Server instance:
|
||||
|
||||
WebAppContext wac = new WebAppContext();
|
||||
... //configure your webapp context
|
||||
JDBCSessionManager jdbcMgr = new JDBCSessionManager();
|
||||
jdbcMgr.setSessionIdManager(server.getSessionIdManager());
|
||||
SessionHandler sessionHandler = new SessionHandler(jdbcMgr);
|
||||
wac.setSessionHandler(sessionHandler);
|
||||
----
|
|
@ -1,240 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[session-clustering-mongodb]]
|
||||
=== Session Clustering with MongoDB
|
||||
|
||||
Jetty can support session clustering by persisting sessions into http://www.mogodb.org[MongoDB].
|
||||
Each Jetty instance locally caches sessions for which it has received requests, writing any changes to the session through to the cluster as the request exits the server.
|
||||
Sessions must obey the Serialization contract, and servlets must call the `Session.setAttribute()` method to ensure that changes are persisted.
|
||||
|
||||
The session persistence mechanism works in conjunction with a load balancer that supports stickiness.
|
||||
Stickiness can be based on various data items, such as source IP address or characteristics of the session ID or a load-balancer specific mechanism.
|
||||
For those load balancers that examine the session ID, the Jetty persistent session mechanism appends a node ID to the session ID, which can be used for routing.
|
||||
|
||||
In this type of solution, the traffic on the network needs to be carefully watched and tends to be the bottleneck.
|
||||
You are probably investigating this solution in order to scale to large amount of users and sessions, so careful attention should be paid to your usage scenario.
|
||||
Applications with a heavy write profile to their sessions will consume more network bandwidth than profiles that are predominately read oriented.
|
||||
We recommend using this session manager with largely read based session scenarios.
|
||||
|
||||
==== Configuration
|
||||
|
||||
There are two components to session management in Jetty: a session ID manager and a session manager.
|
||||
|
||||
* The session ID manager ensures that session IDs are unique across all webapps hosted on a Jetty instance, and thus there can only be one session ID manager per Jetty instance.
|
||||
* The session manager handles the session lifecycle (create/update/invalidate/expire) on behalf of a web application, so there is one session manager per web application instance.
|
||||
|
||||
These managers also cooperate and collaborate with the `org.eclipse.jetty.server.session.SessionHandler` to enable cross-context dispatch.
|
||||
|
||||
==== The nosql Module
|
||||
|
||||
When using the jetty distribution, to enable the MongoDB session persistence mechanism, you will first need to enable the nosql link:#startup-modules[module] for your link:#creating-jetty-base[base] using the `--add-to-start` argument to the link:#startup-overview[start.jar].
|
||||
This module will automatically download the `mongodb-java-driver` and install it to your base's `lib/nosql` directory.
|
||||
|
||||
As part of the module installation, the necessary mongo java driver jars will be dynamically downloaded and installed to your `${jetty.base}/lib/nosql` directory.
|
||||
If you need to up or downgrade the version of these jars, then you can delete the jars that were automatically installed and replace them.
|
||||
Once you've done that, you will need to prevent Jetty's startup checks from detecting the missing jars.
|
||||
To do that, you can use `--skip-file-validation=nosql` argument to start.jar on the command line, or place that line inside `${jetty.base}/start.ini` to ensure it is used for every start.
|
||||
|
||||
You will also find the following properties, either in your base's `start.d/nosql.ini` file or appended to your `start.ini`, depending on how you enabled the module:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
## Unique identifier for this node in the cluster
|
||||
jetty.nosqlSession.workerName=node1
|
||||
|
||||
|
||||
## Interval in seconds between scavenging expired sessions
|
||||
jetty.nosqlSession.scavenge=1800
|
||||
----
|
||||
|
||||
The `jetty.nosqlSession.workerName` is the unique name for this Jetty Server instance.
|
||||
It will be used by the sticky load balancer to uniquely identify the node.
|
||||
You should change this value on *each* node to which you install MongoDB session management.
|
||||
|
||||
The `jetty.nosqlSession.scavenge` property defines the time in seconds between runs of the scavenger: the scavenger is a task which runs periodically to clean out sessions that have expired but become stranded in the database for whatever reason.
|
||||
|
||||
These properties are substituted into the configuration of the `MongoDBSessionIdManager` and `MongoSessionManager`.
|
||||
|
||||
===== Configuring the MongoSessionIdManager
|
||||
|
||||
The nosql module will have installed file called `$\{jetty.home}/etc/jetty-nosql.xml`.
|
||||
This file configures an instance of the `MongoSessionIdManager` that will be shared across all webapps deployed on that server.
|
||||
It looks like this:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
include::{SRCDIR}/jetty-nosql/src/main/config/etc/jetty-nosql.xml[]
|
||||
----
|
||||
|
||||
The `MongoSessionIdManager` needs access to a MongoDB cluster, and the `jetty-nosql.xml` file assumes the defaults of localhost and default MongoDB port.
|
||||
If you need to configure something else, you will need to edit this file.
|
||||
Here's an example of a more complex setup to use a remote MongoDB instance:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
<New id="mongodb" class="com.mongodb.Mongo">
|
||||
<Arg>
|
||||
<New class="java.util.ArrayList">
|
||||
<Call name="add">
|
||||
<Arg>
|
||||
<New class="com.mongodb.ServerAddress">
|
||||
<Arg type="java.lang.String">foo.example.com</Arg>
|
||||
<Arg type="int">27017</Arg>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
<!-- Add more Call statements here as desired --> </New>
|
||||
</Arg>
|
||||
|
||||
<Call name="getDB">
|
||||
<Arg>HttpSessions</Arg>
|
||||
<Call id="sessionDocument" name="getCollection">
|
||||
<Arg>sessions</Arg>
|
||||
</Call>
|
||||
</Call>
|
||||
<!-- If you want to configure Jetty to be able to read through the slaves, call the following: -->
|
||||
<Call name="slaveOk"/>
|
||||
</New>
|
||||
|
||||
<Set name="sessionIdManager">
|
||||
<New id="mongoIdMgr" class="org.eclipse.jetty.nosql.mongodb.MongoSessionIdManager">
|
||||
<Arg>
|
||||
<Ref id="Server"/>
|
||||
</Arg>
|
||||
<Arg>
|
||||
<Ref id="sessionDocument"/>
|
||||
</Arg>
|
||||
<Set name="workerName"><Property name="jetty.nosqlSession.workerName" default="node1"/></Set>
|
||||
<Set name="scavengePeriod"><Property name="jetty.nosqlSession.scavenge" default="1800"/></Set>
|
||||
</New>
|
||||
</Set>
|
||||
|
||||
|
||||
----
|
||||
|
||||
As Jetty configuration files are direct mappings of XML to Java, it is straight forward to do this in code:
|
||||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
|
||||
Server server = new Server();
|
||||
...
|
||||
MongoSessionIdManager idMgr = newMongoSessionIdManager(server);
|
||||
idMgr.setWorkerName("node1");
|
||||
idMgr.setScavengePeriod(1800);
|
||||
server.setSessionIdManager(idMgr);
|
||||
|
||||
----
|
||||
|
||||
The MongoSessionIdManager has slightly different options than some of our more traditional session options.
|
||||
The `MongoDBSessionIdManager` has the same scavenge timers which govern the setting of a valid session to invalid after a certain period of inactivity.
|
||||
New to this session id manager is the extra purge setting which governs removal from the MongoDB cluster.
|
||||
This can be configured through the 'purge' option. Purge is by default set to true and by default runs daily for each node on the cluster.
|
||||
Also able to be configured is the age in which an invalid session will be retained which is set to 1 day by default.
|
||||
This means that invalid sessions will be removed after lingering in the MongoDB instance for a day.
|
||||
There is also an option for purging valid sessions that have not been used recently.
|
||||
The default time for this is 1 week. You can disable these behaviors by setting purge to false.
|
||||
|
||||
scavengeDelay::
|
||||
How long to delay before periodically looking for sessions to scavenge?
|
||||
scavengePeriod::
|
||||
How much time after a scavenge has completed should you wait before doing it again?
|
||||
scavengeBlockSize::
|
||||
Number of session ids to which to limit each scavenge query.
|
||||
If you have a very large number of sessions in memory then setting this to a non 0 value may help speed up scavenging by breaking the scavenge into multiple, queries.
|
||||
The default is 0, which means that all session ids are considered in a single query.
|
||||
purge (Boolean)::
|
||||
Do you want to purge (delete) sessions that are invalid from the session store completely?
|
||||
purgeDelay::
|
||||
How often do you want to perform this purge operation?
|
||||
purgeInvalidAge::
|
||||
How old should an invalid session be before it is eligible to be purged?
|
||||
purgeValidAge::
|
||||
How old should a valid session be before it is eligible to be marked invalid and purged?
|
||||
Should this occur at all?
|
||||
purgeLimit::
|
||||
Integer value that represents how many items to return from a purge query.
|
||||
The default is 0, which is unlimited.
|
||||
If you have a lot of old expired orphaned sessions then setting this value may speed up the purge process.
|
||||
preserveOnStop::
|
||||
Whether or not to retain all sessions when the session manager stops.
|
||||
Default is `true`.
|
||||
|
||||
===== Configuring a MongoSessionManager
|
||||
|
||||
As mentioned elsewhere, there should be one `MongoSessionManager` per context (e.g. webapp).
|
||||
It will need to reference the single `MongoSessionIdManager` configured previously for the Server.
|
||||
|
||||
The way you configure a link:{JDURL}/org/eclipse/jetty/nosql/MongoSessionManager.html[org.eclipse.jetty.nosql.mongodb.MongoSessionManager] depends on whether you're configuring from a link:#deployable-descriptor-file[context xml] file or a link:#jetty-web-xml-config[jetty-web.xml] file or code.
|
||||
The basic difference is how you get a reference to the Jetty `org.eclipse.jetty.server.Server` instance.
|
||||
|
||||
From a context xml file, you reference the Server instance as a Ref:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
<Ref name="Server" id="Server">
|
||||
<Call id="mongoIdMgr" name="getSessionIdManager"/>
|
||||
</Ref>
|
||||
|
||||
<Set name="sessionHandler">
|
||||
<New class="org.eclipse.jetty.server.session.SessionHandler">
|
||||
<Arg>
|
||||
<New id="mongoMgr" class="org.eclipse.jetty.nosql.mongodb.MongoSessionManager">
|
||||
<Set name="sessionIdManager">
|
||||
<Ref id="mongoIdMgr"/>
|
||||
</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</New>
|
||||
</Set>
|
||||
----
|
||||
|
||||
From a `WEB-INF/jetty-web.xml` file, you can reference the Server instance directly:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
<Get name="server">
|
||||
<Get id="mongoIdMgr" name="sessionIdManager"/>
|
||||
</Get>
|
||||
<Set name="sessionHandler">
|
||||
<New class="org.eclipse.jetty.server.session.SessionHandler">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.nosql.mongodb.MongoSessionManager">
|
||||
<Set name="sessionIdManager">
|
||||
<Ref id="mongoIdMgr"/>
|
||||
</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</New>
|
||||
</Set>
|
||||
----
|
||||
|
||||
If you're embedding this in code:
|
||||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
//assuming you have already set up the MongoSessionIdManager as shown earlier
|
||||
//and have a reference to the Server instance:
|
||||
|
||||
WebAppContext wac = new WebAppContext();
|
||||
... //configure your webapp context
|
||||
MongoSessionManager mongoMgr = new MongoSessionManager();
|
||||
mongoMgr.setSessionIdManager(server.getSessionIdManager());
|
||||
wac.setSessionHandler(new SessionHandler(mongoMgr));
|
||||
----
|
|
@ -1,281 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[setting-session-characteristics]]
|
||||
=== Setting Session Characteristics
|
||||
|
||||
Sessions are a concept within the Servlet api which allow requests to store and retrieve information across the time a user spends in an application.
|
||||
Choosing the correct session manager implementation is an important consideration for every application as each can fit and perform optimally in different situations.
|
||||
If you need a simple in-memory session manager that can persist to disk then the `HashSessionManager` can be a good place to start.
|
||||
If you need a session manager that can work in a clustered scenario with multiple instances of Jetty, then the JDBC session manager can be an excellent option.
|
||||
Jetty also offers more niche session managers that leverage backends such as MongoDB, Inifinispan, or even Google's Cloud Data Store.
|
||||
|
||||
To modify the session characteristics of a web application, you can use the following parameters, applying them as in one of the example configurations:
|
||||
|
||||
[[using-init-parameters]]
|
||||
==== Using Init Parameters
|
||||
|
||||
Use these parameters to set session characteristics.
|
||||
|
||||
.Init Parameters
|
||||
[cols=",,",options="header",]
|
||||
|=======================================================================
|
||||
|Context Parameter |Default Value |Description
|
||||
|org.eclipse.jetty.servlet.SessionCookie |JSESSIONID |Session cookie
|
||||
name defaults to JSESSIONID, but can be set for a particular webapp with
|
||||
this context param.
|
||||
|
||||
|org.eclipse.jetty.servlet.SessionIdPathParameterName |jsessionid
|
||||
|Session URL parameter name. Defaults to jsessionid, but can be set for
|
||||
a particular webapp with this context param. Set to "none" to disable
|
||||
URL rewriting.
|
||||
|
||||
|org.eclipse.jetty.servlet.SessionDomain |- |Session Domain. If this
|
||||
property is set as a ServletContext param, then it is used as the domain
|
||||
for session cookies.If it is not set, then no domain is specified for
|
||||
the session cookie.
|
||||
|
||||
|org.eclipse.jetty.servlet.SessionPath |- |Session Path. If this
|
||||
property is set as a ServletContext param, then it is used as the path
|
||||
for the session cookie. If it is not set, then the context path is used
|
||||
as the path for the cookie.
|
||||
|
||||
|org.eclipse.jetty.servlet.MaxAge |-1 |Session Max Age. If this property
|
||||
is set as a ServletContext param, then it is used as the max age for the
|
||||
session cookie. If it is not set, then a max age of -1 is used.
|
||||
|
||||
|org.eclipse.jetty.servlet.CheckingRemoteSessionIdEncoding |false |If
|
||||
true, Jetty will add JSESSIONID parameter even when encoding external
|
||||
urls with calls to encodeURL(). False by default.
|
||||
|=======================================================================
|
||||
|
||||
[[applying-init-parameters]]
|
||||
===== Applying Init Parameters
|
||||
|
||||
The following sections provide examples of how to apply the init parameters.
|
||||
|
||||
[[context-parameter-example]]
|
||||
====== Context Parameter Example
|
||||
|
||||
You can set these parameters as context parameters in a web application's `WEB-INF/web.xml` file:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app
|
||||
xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
|
||||
version="2.5">
|
||||
...
|
||||
<context-param>
|
||||
<param-name>org.eclipse.jetty.servlet.SessionCookie</param-name>
|
||||
<param-value>XSESSIONID</param-value>
|
||||
</context-param>
|
||||
<context-param>
|
||||
<param-name>org.eclipse.jetty.servlet.SessionIdPathParameterName</param-name>
|
||||
<param-value>xsessionid</param-value>
|
||||
</context-param>
|
||||
...
|
||||
</web-app>
|
||||
|
||||
|
||||
----
|
||||
|
||||
[[web-application-examples]]
|
||||
====== Web Application Examples
|
||||
|
||||
You can configure init parameters on a web application, either in code, or in a Jetty context xml file equivalent:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
|
||||
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
|
||||
<Set name="contextPath">/test</Set>
|
||||
<Set name="war"><SystemProperty name="jetty.home" default="."/>/webapps/test</Set>
|
||||
|
||||
...
|
||||
|
||||
<Call name="setInitParameter">
|
||||
<Arg>org.eclipse.jetty.servlet.SessionCookie</Arg>
|
||||
<Arg>XSESSIONID</Arg>
|
||||
</Call>
|
||||
<Call name="setInitParameter">
|
||||
<Arg>org.eclipse.jetty.servlet.SessionIdPathParameterName</Arg>
|
||||
<Arg>xsessionid</Arg>
|
||||
</Call>
|
||||
</Configure>
|
||||
|
||||
|
||||
----
|
||||
|
||||
[[init-parameter-examples]]
|
||||
====== SessionManager Examples
|
||||
|
||||
You can configure init parameters directly on a `SessionManager` instance, either in code or the equivalent in xml:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
|
||||
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
|
||||
<Set name="contextPath">/test</Set>
|
||||
<Set name="war"><SystemProperty name="jetty.home" default="."/>/webapps/test</Set>
|
||||
|
||||
...
|
||||
|
||||
<Get name="sessionHandler">
|
||||
<Set name="sessionManager">
|
||||
<New class="org.eclipse.jetty.server.session.HashSessionManager">
|
||||
<Set name="sessionCookie">XSESSIONID</Set>
|
||||
<Set name="sessionIdPathParameterName">xsessionid</Set>
|
||||
</New>
|
||||
</Set>
|
||||
</Get>
|
||||
</Configure>
|
||||
|
||||
|
||||
----
|
||||
|
||||
==== Using Servlet 3.0 Session Configuration
|
||||
|
||||
With the advent of http://jcp.org/en/jsr/detail?id=315[Servlet Specification 3.0] there are new APIs for configuring session handling characteristics.
|
||||
What was achievable before only via Jetty-specific link:#session-init-params[init-parameters] can now be achieved in a container-agnostic manner either in code, or via `web.xml`.
|
||||
|
||||
[[session-cookie-configuration]]
|
||||
===== SessionCookieConfiguration
|
||||
|
||||
The http://docs.oracle.com/javaee/6/api/javax/servlet/SessionCookieConfig.html[javax.servlet.SessionCookieConfig] class can be used to set up session handling characteristics.
|
||||
For full details, consult the http://docs.oracle.com/javaee/6/api/javax/servlet/SessionCookieConfig.html[javadoc].
|
||||
|
||||
Below is an example of this implementation: a `ServletContextListener` retrieves the `SessionCookieConfig` and sets up some new values when the context is being initialized:
|
||||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
import javax.servlet.SessionCookieConfig;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
|
||||
public class TestListener implements ServletContextListener
|
||||
{
|
||||
|
||||
public void contextInitialized(ServletContextEvent sce)
|
||||
{
|
||||
String comment = "This is my special cookie configuration";
|
||||
String domain = "foo.com";
|
||||
String path = "/my/special/path";
|
||||
boolean isSecure = true;
|
||||
boolean httpOnly = false;
|
||||
int maxAge = 30000;
|
||||
String cookieName = "FOO_SESSION";
|
||||
|
||||
|
||||
SessionCookieConfig scf = sce.getServletContext().getSessionCookieConfig();
|
||||
|
||||
scf.setComment(comment);
|
||||
scf.setDomain(domain);
|
||||
scf.setHttpOnly(httpOnly);
|
||||
scf.setMaxAge(maxAge);
|
||||
scf.setPath(path);
|
||||
scf.setSecure(isSecure);
|
||||
scf.setName(cookieName);
|
||||
}
|
||||
|
||||
public void contextDestroyed(ServletContextEvent sce)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
You can also use `web.xml` to configure the session handling characteristics instead: here's an example doing exactly the same as above instead of using code:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app
|
||||
xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
||||
metadata-complete="true"
|
||||
version="3.0">
|
||||
|
||||
<session-config>
|
||||
<cookie-config>
|
||||
<comment>This is my special cookie configuration</comment>
|
||||
<domain>foo.com</domain>
|
||||
<http-only>false</http-only>
|
||||
<max-age>30000</max-age>
|
||||
<path>/my/special/path</path>
|
||||
<secure>true</secure>
|
||||
<name>FOO_SESSION</name>
|
||||
</cookie-config>
|
||||
</session-config>
|
||||
</web-app>
|
||||
----
|
||||
|
||||
[[session-tracking-modes]]
|
||||
===== SessionTrackingModes
|
||||
|
||||
In addition to the configuration of link:#session-cookie-configuration[session cookies], since Servlet 3.0 you can also use the http://docs.oracle.com/javaee/6/api/javax/servlet/SessionTrackingMode.html[javax.servlet.SessionTrackingMode] to configure session tracking.
|
||||
|
||||
To determine what are the _default_ session tracking characteristics used by the container, call:
|
||||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
javax.servlet.SessionContext.getDefaultSessionTrackingModes();
|
||||
----
|
||||
|
||||
This returns a java.util.Set of javax.servlet.SessionTrackingMode. The
|
||||
_default_ session tracking modes for Jetty are:
|
||||
|
||||
* http://docs.oracle.com/javaee/6/api/javax/servlet/SessionTrackingMode.html#COOKIE[SessionTrackingMode.COOKIE]
|
||||
* http://docs.oracle.com/javaee/6/api/javax/servlet/SessionTrackingMode.html#URL[SessionTrackingMode.URL]
|
||||
|
||||
To see which session tracking modes are actually in effect for this Context, the following call returns a `java.util.Set` of `javax.servlet.SessionTrackingMode`:
|
||||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
javax.servlet.SessionContext.getEffectiveSessionTrackingModes();
|
||||
----
|
||||
|
||||
To change the session tracking modes, call:
|
||||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
javax.servlet.SessionContext.setSessionTrackingModes(Set<SessionTrackingMode>);
|
||||
----
|
||||
|
||||
You may also set the tracking mode in `web.xml`, e.g.:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app
|
||||
xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
||||
metadata-complete="true"
|
||||
version="3.0">
|
||||
|
||||
<session-config>
|
||||
<tracking-mode>URL</tracking-mode>
|
||||
<tracking-mode>COOKIE</tracking-mode>
|
||||
</session-config>
|
||||
</web-app>
|
||||
----
|
|
@ -1,117 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[using-persistent-sessions]]
|
||||
=== Using Persistent Sessions
|
||||
|
||||
It is sometimes useful to preserve existing Sessions across restarts of Jetty.
|
||||
The link:{JDURL}/org/eclipse/jetty/server/session/HashSessionManager.html[`HashSessionManager`] supports this feature.
|
||||
If you enable persistence, the `HashSessionManager` saves all existing, valid Sessions to disk before shutdown completes.
|
||||
On restart, Jetty restores the saved Sessions.
|
||||
|
||||
[[enabling-persistence]]
|
||||
==== Enabling Persistence
|
||||
|
||||
A `SessionManager` does just what its name suggests – it manages the lifecycle and state of sessions on behalf of a webapp.
|
||||
Each webapp must have its own unique `SessionManager` instance.
|
||||
Enabling persistence is as simple as configuring the `HashSessionManager` as the `SessionManager` for a webapp and telling it where on disk to store the sessions:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
|
||||
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
|
||||
.
|
||||
.
|
||||
.
|
||||
<Set name="sessionHandler">
|
||||
<New class="org.eclipse.jetty.server.session.SessionHandler">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.server.session.HashSessionManager">
|
||||
<Set name="storeDirectory">your/chosen/directory/goes/here</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</New>
|
||||
</Set>
|
||||
.
|
||||
.
|
||||
.
|
||||
</Configure>
|
||||
|
||||
|
||||
----
|
||||
|
||||
The above uses an example of a xref:intro-jetty-configuration-contexts[context configuration file].
|
||||
|
||||
[TIP]
|
||||
====
|
||||
If you want to persist the sessions from multiple webapps:
|
||||
|
||||
1. Configure a separate `HashSessionManager` for each.
|
||||
|
||||
2. Assign to each a different value for `storeDirectory`.
|
||||
====
|
||||
|
||||
[[delaying-session-load]]
|
||||
==== Delaying Session Load
|
||||
|
||||
You might need to ensure that the sessions are loaded AFTER the servlet environment starts up (by default, Jetty eagerly loads sessions as part of the container startup, but before it initializes the servlet environment).
|
||||
For example, the Wicket web framework requires the servlet environment to be available when sessions are activated.
|
||||
|
||||
Using `SessionManager.setLazyLoad(true)`, Jetty loads sessions lazily either when it receives the first request for a session, or the session scavenger runs for the first time, whichever happens first.
|
||||
Here's how the configuration looks in XML:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
<Set name="sessionHandler">
|
||||
<New class="org.eclipse.jetty.server.session.SessionHandler">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.server.session.HashSessionManager">
|
||||
<Set name="lazyLoad">true</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</New>
|
||||
</Set>
|
||||
----
|
||||
|
||||
[[enabling-persistence-for-jetty-maven-plugin]]
|
||||
==== Enabling Persistence for the Jetty Maven Plugin
|
||||
|
||||
To enable session persistence for the Jetty Maven plugin, set up the `HashSessionManager` in the configuration section like so:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
<plugin>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-maven-plugin</artifactId>
|
||||
<version>9.0.0.RC2 (or current version)</version>
|
||||
<configuration>
|
||||
<!-- ... -->
|
||||
<webAppConfig implementation="org.eclipse.jetty.maven.plugin.JettyWebAppContext">
|
||||
<defaultsDescriptor>${project.build.outputDirectory}/META-INF/webdefault.xml</defaultsDescriptor>
|
||||
<contextPath>${jetty.contextRoot}</contextPath>
|
||||
<sessionHandler implementation="org.eclipse.jetty.server.session.SessionHandler">
|
||||
<sessionManager implementation="org.eclipse.jetty.server.session.HashSessionManager">
|
||||
<storeDirectory>${project.basedir}/target/jetty-sessions</storeDirectory>
|
||||
<idleSavePeriod>1</idleSavePeriod>
|
||||
</sessionManager>
|
||||
</sessionHandler>
|
||||
</webAppConfig>
|
||||
<!-- ... -->
|
||||
</configuration>
|
||||
</plugin>
|
||||
----
|
|
@ -1,89 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[configuring-sessions-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
|
||||
|
||||
When using the Jetty distribution, you will first need to enable the `session-store-file` link:#startup-modules[module] for your link:#startup-base-and-home[Jetty base] using the `--add-to-start` argument on the command line.
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
$ java -jar ../start.jar --create-startd
|
||||
INFO : Base directory was modified
|
||||
|
||||
$ java -jar ../start.jar --add-to-start=session-store-file
|
||||
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-file initialized in ${jetty.base}/start.d/session-store-file.ini
|
||||
MKDIR : ${jetty.base}/sessions
|
||||
INFO : Base directory was modified
|
||||
----
|
||||
|
||||
Doing this enables the File System Session module and any dependent modules or files needed for it to run on the server.
|
||||
The example above is using a fresh `${jetty.base}` with nothing else enabled.
|
||||
|
||||
When the `--add-to-start` argument was added to the command line, it enabled the the `session-store-file` module as well as the `sessions` and `server` modules, which are required for the File System session management to operate.
|
||||
Additionally a `${jetty.base}/sessions` directory was created.
|
||||
By default Session files will be saved to this directory.
|
||||
|
||||
In addition to adding these modules to the classpath of the server, several ini configuration files were added to the `${jetty.base}/start.d` directory.
|
||||
|
||||
____
|
||||
[NOTE]
|
||||
Session data is now only loaded when requested.
|
||||
Previous functionality such as `setLazyLoad` has been removed.
|
||||
____
|
||||
|
||||
==== Configuring File System Session Properties
|
||||
|
||||
Opening `start.d/session-store-file.ini` will show a list of all the configurable options for the file system session module:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
# ---------------------------------------
|
||||
# Module: session-store-file
|
||||
# Enables session persistent storage in files.
|
||||
# ---------------------------------------
|
||||
--module=session-store-file
|
||||
|
||||
jetty.session.file.storeDir=${jetty.base}/sessions
|
||||
#jetty.session.file.deleteUnrestorableFiles=false
|
||||
#jetty.session.savePeriod.seconds=0
|
||||
----
|
||||
|
||||
jetty.session.storeDir::
|
||||
This defines the location for storage of Session files.
|
||||
jetty.session.file.deleteUnrestorableFiles::
|
||||
Boolean.
|
||||
If set to true, unreadable files will be deleted: this is useful to prevent repeated logging of the same error when the scavenger periodically (re-) attempts to load the corrupted information for a session in order to expire it.
|
||||
jetty.session.savePeriod.seconds=0::
|
||||
By default whenever the last concurrent request leaves a session, that session is always persisted via the `SessionDataStore`, even if the only thing that changed on the session is its updated last access time.
|
||||
A non-zero value means that the `SessionDataStore` will skip persisting the session if only the access time changed, and it has been less than `savePeriod` seconds since the last time the session was written.
|
||||
+
|
||||
____
|
||||
[NOTE]
|
||||
Configuring `savePeriod` is useful if your persistence technology is very slow/costly for writes.
|
||||
In a clustered environment, there is a risk of the last access time of the session being out-of-date in the shared store for up to `savePeriod` seconds.
|
||||
This allows the possibility that a node may prematurely expire the session, even though it is in use by another node.
|
||||
Thorough consideration of the `maxIdleTime` of the session when setting the `savePeriod` is imperative - there is no point in setting a `savePeriod` that is larger than the `maxIdleTime`.
|
||||
____
|
|
@ -1,271 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[configuring-sessions-gcloud]]
|
||||
|
||||
=== Persistent Sessions: Google Cloud DataStore
|
||||
|
||||
==== Preparation
|
||||
|
||||
You will first need to create a project and enable the Google Cloud api: https://cloud.google.com/docs/authentication#preparation.
|
||||
Take note of the project id that you create in this step as you need to supply it in later steps.
|
||||
|
||||
===== Communicating with GCloudDataStore
|
||||
|
||||
====== When running Jetty outside of google infrastructure
|
||||
|
||||
Before running Jetty, you will need to choose one of the following methods to set up the local environment to enable remote GCloud DataStore communications.
|
||||
|
||||
1. Using the GCloud SDK:
|
||||
* Ensure you have the GCloud SDK installed: https://cloud.google.com/sdk/?hl=en.
|
||||
* Use the GCloud tool to set up the project you created in the preparation step: `gcloud config set project PROJECT_ID`
|
||||
* Use the GCloud tool to authenticate a google account associated with the project created in the preparation step: `gcloud auth login ACCOUNT`
|
||||
|
||||
2. Using environment variables
|
||||
* Define the environment variable `GCLOUD_PROJECT` with the project id you created in the preparation step.
|
||||
* Generate a JSON link:https://cloud.google.com/storage/docs/authentication?hl=en#service_accounts[service account key] and then define the environment variable `GOOGLE_APPLICATION_CREDENTIALS=/path/to/my/key.json`
|
||||
|
||||
|
||||
====== When Running Jetty Inside of Google Infrastructure
|
||||
|
||||
The Google deployment tools will automatically configure the project and authentication information for you.
|
||||
|
||||
==== Configuring Indexes for Session Data
|
||||
|
||||
Using some special, composite indexes can speed up session search operations, although it may make write operations slower.
|
||||
By default, indexes will not be used.
|
||||
In order to use them, you will need to manually upload a file that defines the indexes.
|
||||
This file is named `index.yaml` and you can find it in your distribution in `${jetty.base}/etc/sessions/gcloud/index.yaml`.
|
||||
|
||||
Follow the instructions link:https://cloud.google.com/datastore/docs/tools/#the_development_workflow_using_gcloud[here] to upload the pre-generated `index.yaml` file.
|
||||
|
||||
===== Communicating with the GCloudDataStore Emulator
|
||||
|
||||
To enable communication using the GCloud Emulator:
|
||||
|
||||
* Ensure you have the GCloud SDK installed: https://cloud.google.com/sdk/?hl=en
|
||||
* Follow the instructions link:https://cloud.google.com/datastore/docs/tools/datastore-emulator[here] on how to start the GCloud datastore emulator, and how to propagate the environment variables that it creates to the terminal in which you run Jetty.
|
||||
|
||||
==== Enabling the Google Cloud DataStore Module
|
||||
|
||||
When using the Jetty distribution, you will first need to enable the `session-store-gcloud` link:#startup-modules[module] for your link:#startup-base-and-home[Jetty base] using the `--add-to-start` argument on the command line.
|
||||
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
$ java -jar ../start.jar --create-startd
|
||||
INFO : Base directory was modified
|
||||
|
||||
$ java -jar ../start.jar --add-to-start=session-store-gcloud
|
||||
|
||||
ALERT: There are enabled module(s) with licenses.
|
||||
The following 2 module(s):
|
||||
+ contains software not provided by the Eclipse Foundation!
|
||||
+ contains software not covered by the Eclipse Public License!
|
||||
+ has not been audited for compliance with its license
|
||||
|
||||
Module: gcloud
|
||||
+ GCloudDatastore is an open source project hosted on Github and released under the Apache 2.0 license.
|
||||
+ https://github.com/GoogleCloudPlatform/gcloud-java
|
||||
+ http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
Module: slf4j-api
|
||||
+ SLF4J is distributed under the MIT License.
|
||||
+ Copyright (c) 2004-2013 QOS.ch
|
||||
+ All rights reserved.
|
||||
+ Permission is hereby granted, free of charge, to any person obtaining
|
||||
+ a copy of this software and associated documentation files (the
|
||||
+ "Software"), to deal in the Software without restriction, including
|
||||
+ without limitation the rights to use, copy, modify, merge, publish,
|
||||
+ distribute, sublicense, and/or sell copies of the Software, and to
|
||||
+ permit persons to whom the Software is furnished to do so, subject to
|
||||
+ the following conditions:
|
||||
+ The above copyright notice and this permission notice shall be
|
||||
+ included in all copies or substantial portions of the Software.
|
||||
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Proceed (y/N)? y
|
||||
INFO : webapp transitively enabled, ini template available with --add-to-start=webapp
|
||||
INFO : jul-impl transitively enabled
|
||||
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 : servlet transitively enabled
|
||||
INFO : gcloud transitively enabled, ini template available with --add-to-start=gcloud
|
||||
INFO : annotations transitively enabled
|
||||
INFO : plus transitively enabled
|
||||
INFO : slf4j-api transitively enabled
|
||||
INFO : security transitively enabled
|
||||
INFO : gcloud-datastore transitively enabled
|
||||
INFO : jcl-slf4j transitively enabled
|
||||
INFO : session-store-gcloud initialized in ${jetty.base}/start.d/session-store-gcloud.ini
|
||||
INFO : jndi transitively enabled
|
||||
MKDIR : ${jetty.base}/etc
|
||||
COPY : ${jetty.home}/modules/jul-impl/etc/java-util-logging.properties to ${jetty.base}/etc/java-util-logging.properties
|
||||
MKDIR : ${jetty.base}/lib/slf4j
|
||||
DOWNLD: https://repo1.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/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: https://repo1.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/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: https://repo1.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: https://repo1.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: https://repo1.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: https://repo1.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: https://repo1.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/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: https://repo1.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
|
||||
ERROR : Module jcl-slf4j requires a module providing slf4j-impl from one of [slf4j-simple-impl, slf4j-logback, slf4j-jul, slf4j-log4j2, slf4j-log4j]
|
||||
|
||||
ERROR : Unsatisfied module dependencies: jcl-slf4j
|
||||
|
||||
Usage: java -jar $JETTY_HOME/start.jar [options] [properties] [configs]
|
||||
java -jar $JETTY_HOME/start.jar --help # for more information
|
||||
----
|
||||
|
||||
Doing this enables the GCloud Session module and any dependent session modules or files needed for it to run on the server.
|
||||
The example above is using a fresh `${jetty.base}` with nothing else enabled.
|
||||
Because the Google Cloud DataStore is not a technology provided by the Eclipse Foundation, users are prompted to assent to the licenses of the external vendor (Apache in this case).
|
||||
|
||||
You will notice, however, that the above output presented a warning: GCloud requires certain Java Commons Logging features to work correctly.
|
||||
GCloud has a dependency on Java Commons Logging, and by default Jetty will route this through SLF4J.
|
||||
Enabling the GCloud Sessions module will also enable the `jcl-slf4j` module, which sends JCL logging information to SLF4J.
|
||||
It does *not*, however, configure a SLF4J implementation for the users.
|
||||
|
||||
As such, you will also need to enable one of the SLF4J implementation modules listed.
|
||||
In this example, we will enable the `slf4j-simple-impl` module to provide a SLF4J implementation.
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
$ java -jar ../start.jar --add-to-start=slf4j-simple-impl
|
||||
INFO : slf4j-simple-impl initialized in ${jetty.base}/start.d/slf4j-simple-impl.ini
|
||||
INFO : resources transitively enabled
|
||||
DOWNLD: https://repo1.maven.org/maven2/org/slf4j/slf4j-simple/1.7.21/slf4j-simple-1.7.21.jar to ${jetty.base}/lib/slf4j/slf4j-simple-1.7.21.jar
|
||||
MKDIR : ${jetty.base}/resources
|
||||
COPY : ${jetty.home}/modules/slf4j-simple-impl/resources/simplelogger.properties to ${jetty.base}/resources/simplelogger.properties
|
||||
INFO : Base directory was modified
|
||||
----
|
||||
|
||||
When the `--add-to-start` argument was added to the command line the first time, it enabled the the `session-store-gcloud` module as well as several others, such as as `server`, `sessions`, `webapp` and others which are required for GCloud session management to operate; the `slf4j-simple-impl` and its dependent modules were added when the the command was run the second time.
|
||||
|
||||
In addition to adding these modules to the classpath of the server it also added the respective configuration files to the `${jetty.base}start.d` directory.
|
||||
|
||||
____
|
||||
[NOTE]
|
||||
If you have updated versions of the jar files automatically downloaded by Jetty, you can place them in the associated `${jetty.base}/lib/` directory and use the `--skip-file-validation=<module name>` command line option to prevent errors when starting your server.
|
||||
____
|
||||
|
||||
==== Configuring GCloud Session Properties
|
||||
|
||||
Opening the `start.d/session-store-gcloud.ini` will display a list of all the configurable properties for the Google Cloud DataStore module:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
# ---------------------------------------
|
||||
# Module: session-store-gcloud
|
||||
# Enables GCloudDatastore session management.
|
||||
# ---------------------------------------
|
||||
--module=session-store-gcloud
|
||||
|
||||
|
||||
## GCloudDatastore Session config
|
||||
#jetty.session.gracePeriod.seconds=3600
|
||||
#jetty.session.savePeriod.seconds=0
|
||||
#jetty.session.gcloud.maxRetries=5
|
||||
#jetty.session.gcloud.backoffMs=1000
|
||||
#jetty.session.gcloud.namespace=
|
||||
#jetty.session.gcloud.model.kind=GCloudSession
|
||||
#jetty.session.gcloud.model.id=id
|
||||
#jetty.session.gcloud.model.contextPath=contextPath
|
||||
#jetty.session.gcloud.model.vhost=vhost
|
||||
#jetty.session.gcloud.model.accessed=accessed
|
||||
#jetty.session.gcloud.model.lastAccessed=lastAccessed
|
||||
#jetty.session.gcloud.model.createTime=createTime
|
||||
#jetty.session.gcloud.model.cookieSetTime=cookieSetTime
|
||||
#jetty.session.gcloud.model.lastNode=lastNode
|
||||
#jetty.session.gcloud.model.expiry=expiry
|
||||
#jetty.session.gcloud.model.maxInactive=maxInactive
|
||||
#jetty.session.gcloud.model.attributes=attributes
|
||||
----
|
||||
|
||||
jetty.session.gracePeriod.seconds::
|
||||
Amount of time, in seconds, to wait for other nodes to be checked to verify an expired session is in fact expired throughout the cluster before closing it.
|
||||
jetty.session.savePeriod.seconds=0::
|
||||
By default whenever the last concurrent request leaves a session, that session is always persisted via the `SessionDataStore`, even if the only thing that changed on the session is its updated last access time.
|
||||
A non-zero value means that the `SessionDataStore` will skip persisting the session if only the access time changed, and it has been less than `savePeriod` seconds since the last time the session was written.
|
||||
+
|
||||
____
|
||||
[NOTE]
|
||||
Configuring `savePeriod` is useful if your persistence technology is very slow/costly for writes.
|
||||
In a clustered environment, there is a risk of the last access time of the session being out-of-date in the shared store for up to `savePeriod` seconds.
|
||||
This allows the possibility that a node may prematurely expire the session, even though it is in use by another node.
|
||||
Thorough consideration of the `maxIdleTime` of the session when setting the `savePeriod` is imperative - there is no point in setting a `savePeriod` that is larger than the `maxIdleTime`.
|
||||
____
|
||||
|
||||
jetty.session.gcloud.maxRetries::
|
||||
Maxmium number of tries to connect to GCloud DataStore to write sessions.
|
||||
jetty.session.gcloud.backoffMs::
|
||||
Amount of time, in milliseconds, between attempts to connect to the GCloud DataStore to write sessions.
|
||||
jetty.session.gcloud.namespace::
|
||||
Optional.
|
||||
Sets the namespace for GCloud Datastore to use.
|
||||
If set, partitions the visibility of session data between webapps, which is helpful for multi-tenant deployments.
|
||||
More information can be found link:https://cloud.google.com/datastore/docs/concepts/multitenancy[here.]
|
||||
|
||||
The other values listed are simply the names of properties that represent stored session data, and can be changed if needed.
|
|
@ -1,197 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[configuring-sessions-hazelcast]]
|
||||
|
||||
=== Persistent Sessions: Hazelcast
|
||||
|
||||
==== Enabling Hazelcast Sessions
|
||||
|
||||
When using the Jetty distribution, you will first need to enable the `session-store-hazelcast-remote` link:#startup-modules[module] for your link:#startup-base-and-home[Jetty base] using the `--add-to-start` argument on the command line.
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
$ java -jar ../start.jar --create-startd
|
||||
MKDIR : ${jetty.base}/start.d
|
||||
INFO : Base directory was modified
|
||||
|
||||
$ java -jar ../start.jar --add-to-start=session-store-hazelcast-remote
|
||||
|
||||
ALERT: There are enabled module(s) with licenses.
|
||||
The following 1 module(s):
|
||||
+ contains software not provided by the Eclipse Foundation!
|
||||
+ contains software not covered by the Eclipse Public License!
|
||||
+ has not been audited for compliance with its license
|
||||
|
||||
Module: session-store-hazelcast-remote
|
||||
+ Hazelcast is an open source project hosted on Github and released under the Apache 2.0 license.
|
||||
+ https://hazelcast.org/
|
||||
+ http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
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/admin/mvn-repo/com/hazelcast/hazelcast/3.8.2
|
||||
DOWNLD: https://repo1.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/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
|
||||
----
|
||||
|
||||
Doing this enables the remote Hazelcast Session module and any dependent modules or files needed for it to run on the server.
|
||||
The example above is using a fresh `${jetty.base}` with nothing else enabled.
|
||||
Because Hazelcast is not a technology provided by the Eclipse Foundation, users are prompted to assent to the licenses of the external vendor (Apache in this case).
|
||||
|
||||
When the `--add-to-start` argument was added to the command line, it enabled the the `session-store-hazelcast-remote` module as well as the `sessions` and `server` modules, which are required for Hazelcast session management to operate.
|
||||
It also downloaded the needed Hazelcast-specific jar files and created a directory named `${jetty.base}/lib/hazelcast/` to house them.
|
||||
|
||||
In addition to adding these modules to the classpath of the server it also added several ini configuration files to the `${jetty.base}/start.d` directory.
|
||||
|
||||
____
|
||||
[NOTE]
|
||||
If you have updated versions of the jar files automatically downloaded by Jetty, you can place them in the associated `${jetty.base}/lib/` directory and use the `--skip-file-validation=<module name>` command line option to prevent errors when starting your server.
|
||||
____
|
||||
|
||||
==== Configuring Hazelcast Remote Properties
|
||||
|
||||
Opening the `start.d/session-store-hazelcast-remote.ini` will show a list of all the configurable options for the Hazelcast module:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
# ---------------------------------------
|
||||
# Module: session-store-hazelcast-remote
|
||||
# Enables session data store in a remote Hazelcast Map
|
||||
# ---------------------------------------
|
||||
--module=session-store-hazelcast-remote
|
||||
|
||||
#jetty.session.hazelcast.mapName=jetty_sessions
|
||||
#jetty.session.hazelcast.onlyClient=true
|
||||
#jetty.session.hazelcast.configurationLocation=
|
||||
jetty.session.hazelcast.scavengeZombies=false
|
||||
#jetty.session.gracePeriod.seconds=3600
|
||||
#jetty.session.savePeriod.seconds=0
|
||||
----
|
||||
|
||||
jetty.session.hazelcast.mapName::
|
||||
Name of the Map in Hazelcast where sessions will be stored.
|
||||
jetty.session.hazelcast.onlyClient::
|
||||
Hazelcast instance will be configured in client mode
|
||||
jetty.session.hazelcast.configurationLocation::
|
||||
Path to an an Hazelcast xml configuration file
|
||||
jetty.session.hazelcast.scavengeZombies::
|
||||
True/False. `False` by default. If `true`, jetty will use hazelcast queries to find sessions that are no longer being used on any jetty node and whose expiry time has passed. If you enable this option, and your session stores attributes that reference classes from inside your webapp, or jetty classes, you will need to ensure that these classes are available on each of your hazelcast instances.
|
||||
jetty.session.gracePeriod.seconds::
|
||||
Amount of time, in seconds, to wait for other nodes to be checked to verify an expired session is in fact expired throughout the cluster before closing it.
|
||||
jetty.session.savePeriod.seconds=0::
|
||||
By default whenever the last concurrent request leaves a session, that session is always persisted via the `SessionDataStore`, even if the only thing that changed on the session is its updated last access time.
|
||||
A non-zero value means that the `SessionDataStore` will skip persisting the session if only the access time changed, and it has been less than `savePeriod` seconds since the last time the session was written.
|
||||
+
|
||||
____
|
||||
[NOTE]
|
||||
Configuring `savePeriod` is useful if your persistence technology is very slow/costly for writes.
|
||||
In a clustered environment, there is a risk of the last access time of the session being out-of-date in the shared store for up to `savePeriod` seconds.
|
||||
This allows the possibility that a node may prematurely expire the session, even though it is in use by another node.
|
||||
Thorough consideration of the `maxIdleTime` of the session when setting the `savePeriod` is imperative - there is no point in setting a `savePeriod` that is larger than the `maxIdleTime`.
|
||||
|
||||
Be aware using the `scavengeZombies` option that if your session attributes contain classes from inside your webapp (or jetty classes) then you will need to put these classes onto the classpath of all of your hazelcast instances.
|
||||
____
|
||||
|
||||
==== Configuring Embedded Hazelcast Clustering
|
||||
|
||||
During testing, it can be helpful to run an in-process instance of Hazelcast.
|
||||
To enable this you will first need to enable the `session-store-hazelcast-embedded` link:#startup-modules[module] for your link:#startup-base-and-home[Jetty base] using the `--add-to-start` argument on the command line.
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
$ java -jar ../start.jar --create-startd
|
||||
MKDIR : ${jetty.base}/start.d
|
||||
INFO : Base directory was modified
|
||||
$ java -jar ../start.jar --add-to-start=session-store-hazelcast-embedded
|
||||
|
||||
ALERT: There are enabled module(s) with licenses.
|
||||
The following 1 module(s):
|
||||
+ contains software not provided by the Eclipse Foundation!
|
||||
+ contains software not covered by the Eclipse Public License!
|
||||
+ has not been audited for compliance with its license
|
||||
|
||||
Module: session-store-hazelcast-embedded
|
||||
+ Hazelcast is an open source project hosted on Github and released under the Apache 2.0 license.
|
||||
+ https://hazelcast.org/
|
||||
+ http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
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/admin/mvn-repo/com/hazelcast/hazelcast/3.8.2
|
||||
DOWNLD: https://repo1.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/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.
|
||||
The example above is using a fresh `${jetty.base}` with nothing else enabled.
|
||||
Because Hazelcast is not a technology provided by the Eclipse Foundation, users are prompted to assent to the licenses of the external vendor (Apache in this case).
|
||||
|
||||
When the `--add-to-start` argument was added to the command line, it enabled the the `session-store-hazelcast-embedded` module as well as the `sessions` and `server` modules, which are required for Hazelcast session management to operate.
|
||||
It also downloaded the needed Hazelcast-specific jar files and created a directory named `${jetty.base}/lib/hazelcast/` to house them.
|
||||
|
||||
In addition to adding these modules to the classpath of the server it also added several ini configuration files to the `${jetty.base}/start.d` directory.
|
||||
|
||||
==== Configuring Hazelcast Embedded Properties
|
||||
|
||||
Opening the `start.d/start.d/session-store-hazelcast-embedded.ini` will show a list of all the configurable options for the Hazelcast module:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
# ---------------------------------------
|
||||
# Module: session-store-hazelcast-embedded
|
||||
# Enables session data store in an embedded Hazelcast Map
|
||||
# ---------------------------------------
|
||||
--module=session-store-hazelcast-embedded
|
||||
|
||||
#jetty.session.hazelcast.mapName=jetty_sessions
|
||||
#jetty.session.hazelcast.configurationLocation=
|
||||
jetty.session.hazelcast.scavengeZombies=false
|
||||
#jetty.session.gracePeriod.seconds=3600
|
||||
#jetty.session.savePeriod.seconds=0
|
||||
----
|
||||
jetty.session.hazelcast.mapName::
|
||||
Name of the Map in Hazelcast where sessions will be stored.
|
||||
jetty.session.hazelcast.configurationLocation::
|
||||
Path to an an Hazelcast xml configuration file
|
||||
jetty.session.hazelcast.scavengeZombies::
|
||||
True/False. `False` by default. If `true`, jetty will use hazelcast queries to find sessions that are no longer being used on any jetty node and whose expiry time has passed. If you enable this option, and your sessions contain attributes that reference classes from inside your webapp (or jetty classes) you will need to ensure that these classes are available on each of your hazelcast instances.
|
||||
jetty.session.gracePeriod.seconds::
|
||||
Amount of time, in seconds, to wait for other nodes to be checked to verify an expired session is in fact expired throughout the cluster before closing it.
|
||||
jetty.session.savePeriod.seconds=0::
|
||||
By default whenever the last concurrent request leaves a session, that session is always persisted via the `SessionDataStore`, even if the only thing that changed on the session is its updated last access time.
|
||||
A non-zero value means that the `SessionDataStore` will skip persisting the session if only the access time changed, and it has been less than `savePeriod` seconds since the last time the session was written.
|
||||
+
|
||||
____
|
||||
[NOTE]
|
||||
Configuring `savePeriod` is useful if your persistence technology is very slow/costly for writes.
|
||||
In a clustered environment, there is a risk of the last access time of the session being out-of-date in the shared store for up to `savePeriod` seconds.
|
||||
This allows the possibility that a node may prematurely expire the session, even though it is in use by another node.
|
||||
Thorough consideration of the `maxIdleTime` of the session when setting the `savePeriod` is imperative - there is no point in setting a `savePeriod` that is larger than the `maxIdleTime`.
|
||||
|
||||
Be aware using the `scavengeZombies` option that if your session attributes contain classes from inside your webapp (or jetty classes) then you will need to put these classes onto the classpath of all of your hazelcast instances. In the cast of embedded hazelcast, as it is started before your webapp, it will NOT have access to your webapp's classes - you will need to extract these classes and put them onto the jetty server's classpath.
|
||||
____
|
|
@ -1,56 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[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.
|
|
@ -1,248 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[configuring-sessions-infinispan]]
|
||||
|
||||
=== 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.
|
||||
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
$ java -jar ../start.jar --create-startd
|
||||
INFO : Base directory was modified
|
||||
|
||||
$ java -jar ../start.jar --add-to-start=session-store-infinispan-remote
|
||||
|
||||
ALERT: There are enabled module(s) with licenses.
|
||||
The following 1 module(s):
|
||||
+ contains software not provided by the Eclipse Foundation!
|
||||
+ contains software not covered by the Eclipse Public License!
|
||||
+ has not been audited for compliance with its license
|
||||
|
||||
Module: session-store-infinispan-remote
|
||||
+ Infinispan is an open source project hosted on Github and released under the Apache 2.0 license.
|
||||
+ http://infinispan.org/
|
||||
+ http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
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-infinispan-remote initialized in ${jetty.base}/start.d/session-store-infinispan-remote.ini
|
||||
MKDIR : ${jetty.base}/lib/infinispan
|
||||
DOWNLD: https://repo1.maven.org/maven2/org/infinispan/infinispan-remote-it/9.4.8.Final/infinispan-remote-it-9.4.8.Final.jar to ${jetty.base}/lib/infinispan/infinispan-remote-it-9.4.8.Final.jar
|
||||
MKDIR : ${jetty.base}/resources
|
||||
COPY : ${jetty.home}/modules/session-store-infinispan-remote/resources/hotrod-client.properties to ${jetty.base}/resources/hotrod-client.properties
|
||||
INFO : Base directory was modified
|
||||
----
|
||||
|
||||
Doing this enables the remote Infinispan Session module and any dependent modules or files needed for it to run on the server.
|
||||
The example above is using a fresh `${jetty.base}` with nothing else enabled.
|
||||
Because Infinispan is not a technology provided by the Eclipse Foundation, users are prompted to assent to the licenses of the external vendor (Apache in this case).
|
||||
|
||||
When the `--add-to-start` argument was added to the command line, it enabled the the `session-store-infinispan-remote` module as well as the `sessions` and `server` modules, which are required for Infinispan session management to operate.
|
||||
It also downloaded the needed Infinispan-specific jar files and created a directory named `${jetty.base}/lib/infinispan/` to house them.
|
||||
|
||||
In addition to adding these modules to the classpath of the server it also added several ini configuration files to the `${jetty.base}/start.d` directory.
|
||||
|
||||
____
|
||||
[NOTE]
|
||||
If you have updated versions of the jar files automatically downloaded by Jetty, you can place them in the associated `${jetty.base}/lib/` directory and use the `--skip-file-validation=<module name>` command line option to prevent errors when starting your server.
|
||||
____
|
||||
|
||||
==== Configuring Inifinspan Remote Properties
|
||||
|
||||
Opening the `start.d/session-store-infinispan-remote.ini` will show a list of all the configurable options for the JDBC module:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
# ---------------------------------------
|
||||
# Module: session-store-infinispan-remote
|
||||
# Enables session data store in a remote Infinispan cache
|
||||
# ---------------------------------------
|
||||
--module=session-store-infinispan-remote
|
||||
|
||||
#jetty.session.infinispan.remoteCacheName=sessions
|
||||
#jetty.session.infinispan.idleTimeout.seconds=0
|
||||
#jetty.session.gracePeriod.seconds=3600
|
||||
#jetty.session.savePeriod.seconds=0
|
||||
----
|
||||
|
||||
jetty.session.infinispan.remoteCacheName::
|
||||
Name of the cache in Infinispan where sessions will be stored.
|
||||
jetty.session.infinispan.idleTimeout.seconds::
|
||||
Amount of time, in seconds, that a session entry in infinispan can be idle (ie not read or written) before infinispan will delete its entry.
|
||||
Usually, you do *not* want to set a value for this, as you want jetty to handle all session expiration (and call any SessionListeners).
|
||||
However, if there is the possibility that sessions can be left in infinispan but no longer referenced by any jetty node (so called "zombie" or "orphan" sessions), then you might want to use this feature.
|
||||
You should make sure that the number of seconds you specify is sufficiently large to avoid the situation where a session is still being referenced by jetty, but is rarely accessed and thus deleted by infinispan.
|
||||
Alternatively, you can enable the `infinispan-remote-query` module, which will allow jetty to search the infinispan session cache to proactively find and properly (ie calling any SessionListeners) scavenge defunct sessions.
|
||||
jetty.session.gracePeriod.seconds::
|
||||
Amount of time, in seconds, to wait for other nodes to be checked to verify an expired session is in fact expired throughout the cluster before closing it.
|
||||
jetty.session.savePeriod.seconds=0::
|
||||
By default whenever the last concurrent request leaves a session, that session is always persisted via the `SessionDataStore`, even if the only thing that changed on the session is its updated last access time.
|
||||
A non-zero value means that the `SessionDataStore` will skip persisting the session if only the access time changed, and it has been less than `savePeriod` seconds since the last time the session was written.
|
||||
|
||||
____
|
||||
[NOTE]
|
||||
Configuring `savePeriod` is useful if your persistence technology is very slow/costly for writes.
|
||||
In a clustered environment, there is a risk of the last access time of the session being out-of-date in the shared store for up to `savePeriod` seconds.
|
||||
This allows the possibility that a node may prematurely expire the session, even though it is in use by another node.
|
||||
Thorough consideration of the `maxIdleTime` of the session when setting the `savePeriod` is imperative - there is no point in setting a `savePeriod` that is larger than the `maxIdleTime`.
|
||||
____
|
||||
|
||||
==== Configuring the Remote Infinispan Query Module
|
||||
|
||||
Enabling this module allows jetty to search infinispan for expired sessions that are no longer being referenced by any jetty node.
|
||||
Note that this is an *additional* module, to be used in conjuction with the `session-store-infinispan-remote` module.
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
java -jar ../start.jar --add-to-start=infinispan-remote-query
|
||||
----
|
||||
|
||||
There are no configuration properties associated with this module.
|
||||
|
||||
|
||||
==== Configuring Embedded Inifinspan Clustering
|
||||
|
||||
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
|
||||
|
||||
ALERT: There are enabled module(s) with licenses.
|
||||
The following 1 module(s):
|
||||
+ contains software not provided by the Eclipse Foundation!
|
||||
+ contains software not covered by the Eclipse Public License!
|
||||
+ has not been audited for compliance with its license
|
||||
|
||||
Module: session-store-infinispan-embedded
|
||||
+ Infinispan is an open source project hosted on Github and released under the Apache 2.0 license.
|
||||
+ http://infinispan.org/
|
||||
+ http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
Proceed (y/N)? y
|
||||
INFO : server initialised (transitively) in ${jetty.base}/start.d/server.ini
|
||||
INFO : sessions initialised (transitively) in ${jetty.base}/start.d/sessions.ini
|
||||
INFO : session-store-infinispan-embedded initialised in ${jetty.base}/start.d/session-store-infinispan-embedded.ini
|
||||
DOWNLOAD: https://repo1.maven.org/maven2/org/infinispan/infinispan-embedded-it/9.4.8.Final/infinispan-embedded-it-9.4.8.Final.jar to ${jetty.base}/lib/infinispan/infinispan-embedded-it-9.4.8.Final.jar
|
||||
INFO : Base directory was modified
|
||||
----
|
||||
|
||||
Doing this enables the embedded Infinispan Session module and any dependent modules or files needed for it to run on the server.
|
||||
The example above is using a fresh `${jetty.base}` with nothing else enabled.
|
||||
Because Infinispan is not a technology provided by the Eclipse Foundation, users are prompted to assent to the licenses of the external vendor (Apache in this case).
|
||||
|
||||
When the `--add-to-start` argument was added to the command line, it enabled the the `session-store-infinispan-embedded` module as well as the `sessions` and `server` modules, which are required for Infinispan session management to operate.
|
||||
It also downloaded the needed Infinispan-specific jar files and created a directory named `${jetty.base}/lib/infinispan/` to house them.
|
||||
|
||||
In addition to adding these modules to the classpath of the server it also added several ini configuration files to the `${jetty.base}/start.d` directory.
|
||||
|
||||
==== Configuring Inifinspan Embedded Properties
|
||||
|
||||
Opening the `start.d/session-store-infinispan-remote.ini` will show a list of all the configurable options for the JDBC module:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
# ---------------------------------------
|
||||
# Module: session-store-infinispan-embedded
|
||||
# Enables session data store in a local Infinispan cache
|
||||
# ---------------------------------------
|
||||
--module=session-store-infinispan-embedded
|
||||
|
||||
#jetty.session.gracePeriod.seconds=3600
|
||||
#jetty.session.savePeriod.seconds=0
|
||||
----
|
||||
|
||||
jetty.session.gracePeriod.seconds::
|
||||
Amount of time, in seconds, to wait for other nodes to be checked to verify an expired session is in fact expired throughout the cluster before closing it.
|
||||
jetty.session.savePeriod.seconds=0::
|
||||
By default whenever the last concurrent request leaves a session, that session is always persisted via the `SessionDataStore`, even if the only thing that changed on the session is its updated last access time.
|
||||
A non-zero value means that the `SessionDataStore` will skip persisting the session if only the access time changed, and it has been less than `savePeriod` seconds since the last time the session was written.
|
||||
+
|
||||
____
|
||||
[NOTE]
|
||||
Configuring `savePeriod` is useful if your persistence technology is very slow/costly for writes.
|
||||
In a clustered environment, there is a risk of the last access time of the session being out-of-date in the shared store for up to `savePeriod` seconds.
|
||||
This allows the possibility that a node may prematurely expire the session, even though it is in use by another node.
|
||||
Thorough consideration of the `maxIdleTime` of the session when setting the `savePeriod` is imperative - there is no point in setting a `savePeriod` that is larger than the `maxIdleTime`.
|
||||
____
|
||||
|
||||
|
||||
==== Configuring Inifinspan Embedded Query
|
||||
|
||||
Similarly to the `session-store-infinispan-remote` module, the `session-store-infinispan-embedded` module has an adjunct module `infinispan-embedded-query`, which when enabled, will allow jetty to detect and properly scavenge defunct sessions stranded in infinispan.
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
java -jar ../start.jar --add-to-start=infinispan-embedded-query
|
||||
----
|
||||
|
||||
There are no configuration properties associated with this module.
|
||||
|
||||
|
||||
==== Converting Session Format for Jetty-9.4.13
|
||||
|
||||
From Jetty-9.4.13 onwards, we have changed the format of the serialized session when using a remote cache (ie using hotrod).
|
||||
Prior to release 9.4.13 we used the default Infinispan serialization, however this was not able to store sufficient information to allow jetty to properly deserialize session attributes in all circumstances.
|
||||
See issue https://github.com/eclipse/jetty.project/issues/2919 for more background.
|
||||
|
||||
We have provided a conversion program which will convert any sessions stored in Infinispan to the new format.
|
||||
____
|
||||
[IMPORTANT]
|
||||
We recommend that you backup your stored sessions before running the conversion program.
|
||||
____
|
||||
|
||||
How to use the converter:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
java -cp jetty-servlet-api-4.0.2.jar:jetty-util-{VERSION}.jar:jetty-server-{VERSION}.jar:infinispan-remote-9.1.0.Final.jar:jetty-infinispan-{VERSION}.jar:[other classpath] org.eclipse.jetty.session.infinispan.InfinispanSessionLegacyConverter
|
||||
|
||||
Usage: InfinispanSessionLegacyConverter [-Dhost=127.0.0.1] [-Dverbose=true|false] <cache-name> [check]
|
||||
----
|
||||
|
||||
The classpath::
|
||||
Must contain the servlet-api, jetty-util, jetty-server, jetty-infinispan and infinispan-remote jars. If your sessions contain attributes that use application classes, you will also need to also put those classes onto the classpath. If your session has been authenticated, you may also need to include the jetty-security and jetty-http jars on the classpath.
|
||||
Parameters::
|
||||
When used with no arguments the usage message is printed. When used with the `cache-name` parameter the conversion is performed. When used with both `cache-name` and `check` parameters, sessions are checked for whether or not they are converted.
|
||||
|
||||
-Dhost::: you can optionally provide a system property with the address of your remote Infinispan server. Defaults to the localhost.
|
||||
-Dverbose::: defaults to false. If true, prints more comprehensive stacktrace information about failures. Useful to diagnose why a session is not converted.
|
||||
cache-name::: the name of the remote cache containing your sessions. This is mandatory.
|
||||
check::: the optional check command will verify sessions have been converted. Use it _after_ doing the conversion.
|
||||
|
||||
To perform the conversion, run the InfinispanSessionLegacyConverter with just the `cache-name`, and optionally the `host` system property.
|
||||
The following command will attempt to convert all sessions in the cached named `my-remote-cache` on the machine `myhost`, ensuring that application classes in the `/my/custom/classes` directory are on the classpath:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
java -cp jetty-servlet-api-4.0.2.jar:jetty-util-{VERSION}.jar:jetty-server-{VERSION}.jar:infinispan-remote-9.1.0.Final.jar:jetty-infinispan-{VERSION}.jar:/my/custom/classes org.eclipse.jetty.session.infinispan.InfinispanSessionLegacyConverter -Dhost=myhost my-remote-cache
|
||||
----
|
||||
|
||||
If the converter fails to convert a session, an error message and stacktrace will be printed and the conversion will abort. The failed session should be untouched, however _it is prudent to take a backup of your cache before attempting the conversion_.
|
|
@ -1,134 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[configuring-sessions-jdbc]]
|
||||
|
||||
=== Persistent Sessions: JDBC
|
||||
|
||||
==== Enabling JDBC Sessions
|
||||
|
||||
When using the Jetty distribution, you will first need to enable the `session-store-jdbc` link:#startup-modules[module] for your link:#startup-base-and-home[Jetty base] using the `--add-to-start` argument on the command line.
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
$ java -jar ../start.jar --create-startd
|
||||
INFO : Base directory was modified
|
||||
|
||||
$ java -jar ../start.jar --add-to-start=session-store-jdbc
|
||||
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 : sessions/jdbc/datasource dynamic dependency of session-store-jdbc
|
||||
INFO : session-store-jdbc initialized in ${jetty.base}/start.d/session-store-jdbc.ini
|
||||
INFO : Base directory was modified
|
||||
----
|
||||
|
||||
Doing this enables the JDBC Session module and any dependent modules or files needed for it to run on the server.
|
||||
The example above is using a fresh `${jetty.base}` with nothing else enabled.
|
||||
|
||||
When the `--add-to-start` argument was added to the command line, it enabled the the `session-store-jdbc` module as well as the `sessions` and `server` modules, which are required for JDBC session management to operate.
|
||||
|
||||
In addition to adding these modules to the classpath of the server, several ini configuration files were added to the `${jetty.base}/start.d` directory.
|
||||
|
||||
==== Configuring JDBC Session Properties
|
||||
|
||||
Opening the `start.d/session-store-jdbc.ini` will show a list of all the configurable options for the JDBC module:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
# ---------------------------------------
|
||||
# Module: session-store-jdbc
|
||||
# Enables JDBC persistent/distributed session storage.
|
||||
# ---------------------------------------
|
||||
--module=session-store-jdbc
|
||||
|
||||
##
|
||||
##JDBC Session properties
|
||||
##
|
||||
|
||||
#jetty.session.gracePeriod.seconds=3600
|
||||
|
||||
## Connection type:Datasource
|
||||
db-connection-type=datasource
|
||||
#jetty.session.jdbc.datasourceName=/jdbc/sessions
|
||||
|
||||
## Connection type:driver
|
||||
#db-connection-type=driver
|
||||
#jetty.session.jdbc.driverClass=
|
||||
#jetty.session.jdbc.driverUrl=
|
||||
|
||||
## Session table schema
|
||||
#jetty.session.jdbc.schema.accessTimeColumn=accessTime
|
||||
#jetty.session.jdbc.schema.contextPathColumn=contextPath
|
||||
#jetty.session.jdbc.schema.cookieTimeColumn=cookieTime
|
||||
#jetty.session.jdbc.schema.createTimeColumn=createTime
|
||||
#jetty.session.jdbc.schema.expiryTimeColumn=expiryTime
|
||||
#jetty.session.jdbc.schema.lastAccessTimeColumn=lastAccessTime
|
||||
#jetty.session.jdbc.schema.lastSavedTimeColumn=lastSavedTime
|
||||
#jetty.session.jdbc.schema.idColumn=sessionId
|
||||
#jetty.session.jdbc.schema.lastNodeColumn=lastNode
|
||||
#jetty.session.jdbc.schema.virtualHostColumn=virtualHost
|
||||
#jetty.session.jdbc.schema.maxIntervalColumn=maxInterval
|
||||
#jetty.session.jdbc.schema.mapColumn=map
|
||||
#jetty.session.jdbc.schema.table=JettySessions
|
||||
# Optional name of the schema used to identify where the session table is defined in the database:
|
||||
# "" - empty string, no schema name
|
||||
# "INFERRED" - special string meaning infer from the current db connection
|
||||
# name - a string defined by the user
|
||||
#jetty.session.jdbc.schema.schemaName
|
||||
# Optional name of the catalog used to identify where the session table is defined in the database:
|
||||
# "" - empty string, no catalog name
|
||||
# "INFERRED" - special string meaning infer from the current db connection
|
||||
# name - a string defined by the user
|
||||
#jetty.session.jdbc.schema.catalogName
|
||||
----
|
||||
|
||||
jetty.session.gracePeriod.seconds::
|
||||
Amount of time, in seconds, to wait for other nodes to be checked to verify an expired session is in fact expired throughout the cluster before closing it.
|
||||
jetty.session.savePeriod.seconds=0::
|
||||
By default whenever the last concurrent request leaves a session, that session is always persisted via the `SessionDataStore`, even if the only thing that changed on the session is its updated last access time.
|
||||
A non-zero value means that the `SessionDataStore` will skip persisting the session if only the access time changed, and it has been less than `savePeriod` seconds since the last time the session was written.
|
||||
+
|
||||
____
|
||||
[NOTE]
|
||||
Configuring `savePeriod` is useful if your persistence technology is very slow/costly for writes.
|
||||
In a clustered environment, there is a risk of the last access time of the session being out-of-date in the shared store for up to `savePeriod` seconds.
|
||||
This allows the possibility that a node may prematurely expire the session, even though it is in use by another node.
|
||||
Thorough consideration of the `maxIdleTime` of the session when setting the `savePeriod` is imperative - there is no point in setting a `savePeriod` that is larger than the `maxIdleTime`.
|
||||
____
|
||||
|
||||
db-connection-type::
|
||||
Set to either `datasource` or `driver` depending on the type of connection being used.
|
||||
jetty.session.jdbc.datasourceName::
|
||||
Name of the remote datasource.
|
||||
jetty.session.jdbc.driverClass::
|
||||
Name of the JDBC driver that controls access to the remote database, such as `com.mysql.jdbc.Driver`
|
||||
jetty.session.jdbc.driverUrl::
|
||||
Url of the database which includes the driver type, host name and port, service name and any specific attributes unique to the database, such as a username.
|
||||
As an example, here is a mysql connection with the username appended: `jdbc:mysql://127.0.0.1:3306/sessions?user=sessionsadmin`.
|
||||
|
||||
The `jetty.session.jdbc.schema.*` values represent the names of the table and columns in the JDBC database used to store sessions and can be changed to suit your environment.
|
||||
|
||||
There are also two special, optional properties: `jetty.session.jdbc.schema.schemaName` and `jetty.session.jdbc.schema.catalogName`.
|
||||
The exact meaning of these two properties is dependent on your database vendor, but can broadly be described as further scoping for the session table name.
|
||||
See https://en.wikipedia.org/wiki/Database_schema and https://en.wikipedia.org/wiki/Database_catalog.
|
||||
These extra scoping names can come into play at startup time when jetty determines if the session table already exists, or otherwise creates it on-the-fly.
|
||||
If you have employed either of these concepts when you pre-created the session table, or you want to ensure that jetty uses them when it auto-creates the session table, then you have two options: either set them explicitly, or let jetty infer them from a database connection (obtained using either a Datasource or Driver according to the `db-connection-type` you have configured).
|
||||
To set them explicitly, uncomment and supply appropriate values for the `jetty.session.jdbc.schema.schemaName` and/or `jetty.session.jdbc.schema.catalogName` properties.
|
||||
To allow jetty to infer them from a database connection, use the special string `INFERRED` instead.
|
||||
If you leave them blank or commented out, then the sessions table will not be scoped by schema or catalog name.
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[configuring-sessions-memory]]
|
||||
|
||||
=== Non-Persistent Sessions
|
||||
|
||||
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.
|
||||
|
||||
This is delivered by a combination of the `DefaultSessionCache` (to keep sessions in memory) and a `NullSessionDataStore` (to avoid session persistence).
|
||||
|
||||
If you do nothing, Jetty will instantiate one of each of these objects for each context at startup time using hard-coded defaults.
|
||||
|
||||
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.
|
|
@ -1,131 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[configuring-sessions-mongo]]
|
||||
|
||||
=== Persistent Sessions: MongoDB
|
||||
|
||||
==== Enabling MongoDB Sessions
|
||||
|
||||
When using the Jetty distribution, you will first need to enable the `session-store-mongo` link:#startup-modules[module] for your link:#startup-base-and-home[Jetty base] using the `--add-to-start` argument on the command line.
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
$ java -jar ../start.jar --create-startd
|
||||
INFO : Base directory was modified
|
||||
|
||||
$ java -jar ../start.jar --add-to-start=session-store-mongo
|
||||
|
||||
ALERT: There are enabled module(s) with licenses.
|
||||
The following 1 module(s):
|
||||
+ contains software not provided by the Eclipse Foundation!
|
||||
+ contains software not covered by the Eclipse Public License!
|
||||
+ has not been audited for compliance with its license
|
||||
|
||||
Module: session-store-mongo
|
||||
+ The java driver for the MongoDB document-based database system is hosted on GitHub and released under the Apache 2.0 license.
|
||||
+ http://www.mongodb.org/
|
||||
+ http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
||||
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-mongo initialized in ${jetty.base}/start.d/session-store-mongo.ini
|
||||
INFO : sessions/mongo/address dynamic dependency of session-store-mongo
|
||||
MKDIR : ${jetty.base}/lib/nosql
|
||||
DOWNLD: https://repo1.maven.org/maven2/org/mongodb/mongo-java-driver/2.13.2/mongo-java-driver-2.13.2.jar to ${jetty.base}/lib/nosql/mongo-java-driver-2.13.2.jar
|
||||
INFO : Base directory was modified
|
||||
----
|
||||
|
||||
Doing this enables the MongoDB Session module and any dependent modules or files needed for it to run on the server.
|
||||
The example above is using a fresh `${jetty.base}` with nothing else enabled.
|
||||
|
||||
Because MongoDB is not a technology provided by the Eclipse Foundation, users are prompted to assent to the licenses of the external vendor (Apache in this case).
|
||||
When the `--add-to-start` argument was added to the command line, it enabled the the `session-store-mongo` module as well as the `sessions` and `server` modules, which are required for MongoDB session management to operate..
|
||||
It also downloaded the needed Mongo-specific jar file and created a directory named `${jetty.base}/lib/nosql/` to house it.
|
||||
|
||||
In addition to adding these modules to the classpath of the server, several ini configuration files were added to the `${jetty.base}/start.d` directory.
|
||||
|
||||
____
|
||||
[NOTE]
|
||||
If you have updated versions of the jar files automatically downloaded by Jetty, you can place them in the associated `${jetty.base}/lib/` directory and use the `--skip-file-validation=<module name>` command line option to prevent errors when starting your server.
|
||||
____
|
||||
|
||||
==== Configuring MongoDB Session Properties
|
||||
|
||||
Opening the `start.d/session-store-mongo.ini` will show a list of all the configurable options for the MongoDB module:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
----
|
||||
# ---------------------------------------
|
||||
# Module: session-store-mongo
|
||||
# Enables NoSql session management with a MongoDB driver.
|
||||
# ---------------------------------------
|
||||
--module=session-store-mongo
|
||||
|
||||
#jetty.session.mongo.dbName=HttpSessions
|
||||
#jetty.session.mongo.collectionName=jettySessions
|
||||
#jetty.session.gracePeriod.seconds=3600
|
||||
#jetty.session.savePeriod.seconds=0
|
||||
|
||||
connection-type=address
|
||||
#jetty.session.mongo.host=localhost
|
||||
#jetty.session.mongo.port=27017
|
||||
|
||||
#connection-type=uri
|
||||
#jetty.session.mongo.connectionString=mongodb://localhost
|
||||
----
|
||||
|
||||
jetty.session.gracePeriod.seconds::
|
||||
Amount of time, in seconds, to wait for other nodes to be checked to verify an expired session is in fact expired throughout the cluster before closing it.
|
||||
jetty.session.savePeriod.seconds=0::
|
||||
By default whenever the last concurrent request leaves a session, that session is always persisted via the `SessionDataStore`, even if the only thing that changed on the session is its updated last access time.
|
||||
A non-zero value means that the `SessionDataStore` will skip persisting the session if only the access time changed, and it has been less than `savePeriod` seconds since the last time the session was written.
|
||||
+
|
||||
____
|
||||
[NOTE]
|
||||
Configuring `savePeriod` is useful if your persistence technology is very slow/costly for writes.
|
||||
In a clustered environment, there is a risk of the last access time of the session being out-of-date in the shared store for up to `savePeriod` seconds.
|
||||
This allows the possibility that a node may prematurely expire the session, even though it is in use by another node.
|
||||
Thorough consideration of the `maxIdleTime` of the session when setting the `savePeriod` is imperative - there is no point in setting a `savePeriod` that is larger than the `maxIdleTime`.
|
||||
____
|
||||
|
||||
jetty.session.mongo.dbName::
|
||||
Name of the database in Mongo used to store the Session collection.
|
||||
jetty.session.mongo.collectionName::
|
||||
Name of the collection in Mongo used to keep all of the Sessions.
|
||||
jetty.session.gracePeriod.seconds::
|
||||
Amount of time, in seconds, to wait for other nodes to be checked to verify an expired session is in fact expired throughout the cluster before closing it.
|
||||
connection-type=address::
|
||||
Used when utilizing a direct connection to the Mongo server.
|
||||
jetty.session.mongo.host;;
|
||||
Host name or address for the remote Mongo instance.
|
||||
jetty.session.mongo.port;;
|
||||
Port number for the remote Mongo instance.
|
||||
connection-type=uri::
|
||||
Used when utilizing MongoURI for secured connections.
|
||||
jetty.session.mongo.connectionString;;
|
||||
The string defining the MongoURI value, such as `mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]`.
|
||||
More information on how to format the MongoURI string can be found in the https://docs.mongodb.com/manual/reference/connection-string/[official documentation for mongo.]
|
||||
+
|
||||
____
|
||||
[NOTE]
|
||||
You will only use *one* `connection-type` at a time, `address` or `uri`.
|
||||
If both are utilized in your `session-store-mongo.ini`, only the last `connection-type` configured in the file will be used.
|
||||
By default, the `connection-type` of `address` is used.
|
||||
____
|
|
@ -1,99 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[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.flushOnResponseCommit=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`.
|
||||
|
||||
jetty.session.flushOnResponseCommit::
|
||||
Boolean, default `false`.
|
||||
If true, if a session is "dirty" - ie its attributes have changed - it will be written to the backing store as the response is about to commit.
|
||||
This ensures that all subsequent requests whether to the same or different node will see the updated session data.
|
||||
If false, a dirty session will only be written to the backing store when the last simultaneous request for it leaves the session.
|
||||
|
||||
For more general information on the uses of these configuration properties, see link:#sessions-details[Session Components].
|
||||
|
||||
|
||||
==== 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].
|
||||
Configuration options are:
|
||||
|
||||
jetty.session.saveOnCreate::
|
||||
Boolean, default `false`.
|
||||
Controls whether a session that is newly created will be immediately saved to the `SessionDataStore` or lazily saved as the last request for the session exits.
|
||||
|
||||
jetty.session.removeUnloadableSessions::
|
||||
Boolean, default `false`.
|
||||
Controls whether a session that cannot be restored - for example because it is corrupted - from the `SessionDataStore` is deleted by the `SessionDataStore`.
|
||||
|
||||
jetty.session.flushOnResponseCommit::
|
||||
Boolean, default `false`.
|
||||
If true, if a session is "dirty" - ie its attributes have changed - it will be written to the backing store as the response is about to commit.
|
||||
This ensures that all subsequent requests whether to the same or different node will see the updated session data.
|
||||
If false, a dirty session will only be written to the backing store when the last simultaneous request for it leaves the session.
|
||||
|
||||
For more general information on the uses of these configuration properties, see link:#sessions-details[Session Components].
|
|
@ -1,46 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[jetty-sessions-architecture]]
|
||||
=== Session Architecture
|
||||
|
||||
==== Session Architecture Hierarchy
|
||||
|
||||
Each Jetty instance has a singular `SessionIdManager` to handle all session requests, regardless of clustering technology.
|
||||
For each context on the server there is one (1) `SessionCache` which contains all of the Session objects for the given context.
|
||||
The benefit of the `SessionCache` is to ensure that simultaneous requests accessing the same Session Id in the same context always operate on the same Session object.
|
||||
The SessionCache implementation supplied with the Jetty distribution does just that: keeps Session objects in memory so that they can be shared between simultaneous requests.
|
||||
However, it is possible to provide your own implementation that never shares Session objects should you require it.
|
||||
|
||||
Where the `SessionCache` handles Session information, Session data is stored in a `SessionDataStore` that is specific to the clustering technology being implemented.
|
||||
There is only one (1) `SessionDataStore` per `SessionCache`.
|
||||
|
||||
Visually the session architecture can be represented like this:
|
||||
|
||||
image::SessionsHierarchy.png[]
|
||||
|
||||
==== Configuring Sessions in the Jetty Distribution
|
||||
|
||||
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].
|
|
@ -1,103 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[sessions-details]]
|
||||
=== Session Components
|
||||
|
||||
==== SessionIdManager
|
||||
|
||||
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 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.
|
||||
|
||||
See link:#session-configuration-housekeeper[Configuring the SessionIdManager and HouseKeeper] for details on configuration.
|
||||
|
||||
==== 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 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 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.
|
||||
|
||||
`SessionCaches` always write out a Session to the `SessionDataStore` whenever the last request for the Session exits.
|
||||
|
||||
They can also be configured to do an immediate, eager write of a freshly created session.
|
||||
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 link:#session-configuration-sessioncache[configure] the `DefaultSessionCache` to force a write of the Session to the `SessionDataStore` just before the Session is evicted.
|
||||
|
||||
See link:#session-configuration-sessioncache[the L1 Session Cache] for more information.
|
||||
|
||||
==== 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, 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.
|
||||
|
||||
The grace period is an interval, configured in seconds, that attempts to deal with the non-transactional nature of sessions with regard to finding sessions that have expired.
|
||||
Due to the lack of transactionality, in a clustered configuration, even with a sticky load balancer, it is always possible that a Session is live on a node but has not yet been updated in the persistent store.
|
||||
When `SessionDataStores` search their persistent store to find sessions that have expired, they typically perform a few sequential searches:
|
||||
|
||||
* The first verifies the expiration of a list of candidate session ids suggested by the SessionCache
|
||||
* 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.
|
||||
|
||||
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].
|
||||
|
||||
|
||||
==== 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`.
|
||||
|
||||
See link:#session-configuration-memcachedsessiondatastore[the L2 SessionData Cache]for additional information.
|
|
@ -1,95 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[sessions-usecases]]
|
||||
=== Session 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.
|
||||
|
||||
==== Configuring Sessions via Jetty XML
|
||||
|
||||
With the provided session modules, there is no need to configure a context xml or `jetty-web.xml` file for sessions.
|
||||
That said, if a user wishes to configure sessions this way, it is possible using link:#jetty-xml-syntax[Jetty IoC XML format.]
|
||||
|
||||
Below is an example of how you could configure a the link:#configuring-sessions-file-system[`FileSessionDataStore`], but the same concept would apply to any of the *SessionDataStores discussed in this chapter:
|
||||
|
||||
[source, xml, subs="{sub-order}"]
|
||||
----
|
||||
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
|
||||
<Call id="sh" name="getSessionHandler">
|
||||
<Set name="sessionCache">
|
||||
<New class="org.eclipse.jetty.server.session.DefaultSessionCache">
|
||||
<Arg><Ref id="sh"/></Arg>
|
||||
<Set name="sessionDataStore">
|
||||
<New class="org.eclipse.jetty.server.session.FileSessionDataStore">
|
||||
<Set name="storeDir">/tmp/sessions</Set>
|
||||
</New>
|
||||
</Set>
|
||||
</New>
|
||||
</Set>
|
||||
</Call>
|
||||
</Configure>
|
||||
----
|
||||
|
||||
The example above functions in either a `jetty-web.xml` file or a link:#using-basic-descriptor-files[context xml descriptor file.]
|
||||
|
||||
____
|
||||
[NOTE]
|
||||
If you explicitly configure the `SessionCache` and `SessionDataStore` for a `SessionHandler` in a context xml file or `jetty-web.xml` file, any session modules you already have enabled are ignored.
|
||||
So, for example, if you had enabled the `session-store-gcloud module` for your sever, you could force a particular webapp to use the `FileSessionDataStore` by explicitly configuring it in either a context xml file or a `jetty-web.xml` file as shown above.
|
||||
____
|
|
@ -1,178 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[basic-architecture]]
|
||||
=== Jetty Architecture
|
||||
|
||||
==== View from 20,000 feet
|
||||
|
||||
The Jetty link:{JDURL}/org/eclipse/jetty/server/Server.html[Server] is the plumbing between
|
||||
a collection of `Connector`s that accept connections and a collection of `Handler`s that
|
||||
service requests from the connections and produce responses, with threads from a thread pool doing the work.
|
||||
|
||||
image:jetty-high-level-architecture.png[image,width=576]
|
||||
|
||||
While the Jetty request/responses are derived from the Servlet API, the full features of the Servlet API
|
||||
are only available if you configure the appropriate handlers.
|
||||
For example, the session API on the request is inactive unless the request has been passed to a `SessionHandler`.
|
||||
The concept of a Servlet itself is implemented by a `ServletHandler`.
|
||||
If Servlets are not required, there is very little overhead in the use of the servlet request/response APIs.
|
||||
Thus you can build a Jetty server using only connectors and handlers, without using Servlets.
|
||||
|
||||
The job of configuring Jetty is building a tree of connectors and handlers and providing their individual configurations.
|
||||
As Jetty components are simply Plain Old Java Objects (POJOs), you can accomplish this assembly
|
||||
and configuration of components by a variety of techniques:
|
||||
|
||||
* In code, see the examples in the Jetty Source XRef.
|
||||
* Using Jetty XML, a dependency injection style in XML format.
|
||||
* With your dependency injection framework of choice, Spring or XBean.
|
||||
* Using Jetty WebApp and Context Deployers.
|
||||
|
||||
==== Patterns
|
||||
|
||||
The implementation of Jetty follows some fairly standard patterns.
|
||||
Most abstract concepts such as `Connector`s and `Handler`s are captured by interfaces.
|
||||
Generic handling for those interfaces is then provided in an abstract implementation
|
||||
such as `AbstractConnector` and `AbstractHandler`.
|
||||
|
||||
image:basic-architecture-patterns.png[image,width=576]
|
||||
|
||||
The JSR77 inspired life cycle of most Jetty components is represented by the `LifeCycle`
|
||||
interface and the `AbstractLifeCycle` implementation used as the base of many Jetty components.
|
||||
|
||||
==== Connectors
|
||||
|
||||
A `Connector` is the component that accepts TCP connections.
|
||||
For each accepted TCP connection, the `Connector` asks a `ConnectionFactory` to create
|
||||
a `Connection` object that handles the network traffic on that TCP connection, parsing
|
||||
and generating bytes for a specific protocol.
|
||||
|
||||
A `ServerConnector` can therefore be configured with one or more `ConnectionFactory`.
|
||||
|
||||
The simplest case is a single `ConnectionFactory` such as `HttpConnectionFactory`, that
|
||||
creates `HttpConnection` objects that parse and generate bytes for the HTTP/1.1 protocol.
|
||||
|
||||
A more complex case can be a `ServerConnector` configured with three factories:
|
||||
`ProxyConnectionFactory`, `SslConnectionFactory` and `HttpConnectionFactory`.
|
||||
Such connector will be able to handle PROXY protocol bytes coming from a load balancer
|
||||
such as HAProxy (with the `ProxyConnectionFactory`), then handle TLS bytes (with
|
||||
`SslConnectionFactory`) and therefore decrypting/encrypting the bytes from/to a remote
|
||||
client, and finally handling HTTP/1.1 bytes (with `HttpConnectionFactory`).
|
||||
Each `ConnectionFactory` is asked to create a `Connection` object for each TCP connection;
|
||||
the `Connection` objects will be chained together to handle the bytes, each for its
|
||||
own protocol.
|
||||
Therefore the `ProxyConnection` will handle the PROXY protocol bytes, `SslConnection`
|
||||
will handle the encryption/decryption of the bytes, and `HttpConnection` will handle
|
||||
the HTTP/1.1 bytes producing a request and response object that will be processed by
|
||||
applications.
|
||||
|
||||
Advanced usages of Jetty will allow users to write their own `ConnectionFactory` to
|
||||
handle custom protocols that are not implemented directly by the Jetty project,
|
||||
therefore using Jetty as a generic network server.
|
||||
|
||||
==== Handlers
|
||||
|
||||
A `Handler` is the component that deals with HTTP requests and responses.
|
||||
The core API of a handler is the handle method:
|
||||
|
||||
image:basic-architecture-handlers.png[image,width=576]
|
||||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
----
|
||||
|
||||
Parameters:
|
||||
|
||||
* `target` – the target of the request, either a URI or a name.
|
||||
* `baseRequest` – the original unwrapped request object.
|
||||
* `request` – the request object, either as the `baseRequest` object or a wrapper of `baseRequest`.
|
||||
You can use the HttpConnection.getCurrentConnection() method to access the Request object if required.
|
||||
* response – the response object, either unwrapped as `Response` or a wrapper of that response.
|
||||
You can use the HttpConnection.getCurrentConnection() method to access the `Response` object if required.
|
||||
|
||||
An implementation of this method can handle the request, pass the request onto another handler (or servlet)
|
||||
or it might modify and/or wrap the request and then pass it on.
|
||||
This gives three styles of Handler:
|
||||
|
||||
* Coordinating handlers – handlers that route requests to other handlers (`HandlerCollection`, `ContextHandlerCollection`)
|
||||
* Filtering handlers – handlers that augment a request and pass it on to other handlers (`HandlerWrapper`, `ContextHandler`, `SessionHandler`)
|
||||
* Generating handlers – handlers that produce content (`ResourceHandler` and `ServletHandler`)
|
||||
|
||||
===== Nested Handlers and Handlers Called Sequentially
|
||||
|
||||
You can combine handlers to handle different aspects of a request by nesting them,
|
||||
calling them in sequence, or by combining the two models.
|
||||
|
||||
image:basic-architecture-nested-handlers.png[image,width=576]
|
||||
|
||||
Handlers called in sequence perform actions that do not depend on the next invocation, nor on the handler order.
|
||||
They handle a request and generate the response without interacting with other handlers.
|
||||
The main class for this model is `HandlerCollection`.
|
||||
|
||||
Nested handlers are called according to a before/invokeNext/after pattern.
|
||||
The main class for nested handlers is `HandlerWrapper`.
|
||||
Nested handlers are much more common than those called in sequence.
|
||||
|
||||
See also xref:writing-custom-handlers[].
|
||||
|
||||
===== Servlet Handler
|
||||
|
||||
The `ServletHandler` is a `Handler` that generates content by passing the request to any
|
||||
configured Servlet Filters and then to a Servlet mapped by a URI pattern.
|
||||
|
||||
image:basic-architecture-servlet-handler.png[image,width=576]
|
||||
|
||||
A `ServletHandler` is normally deployed within the scope of a `ServletContext`, which is a
|
||||
`ContextHandler` that provides convenience methods for mapping URIs to servlets.
|
||||
|
||||
Filters and Servlets can also use a `RequestDispatcher` to reroute a request to another context
|
||||
or another Servlet in the current context.
|
||||
|
||||
[[what-is-a-context]]
|
||||
==== Contexts
|
||||
|
||||
Contexts are handlers that group other handlers below a particular URI context path or a virtual host.
|
||||
Typically a context can have:
|
||||
|
||||
* A context path that defines which requests are handled by the context (e.g. `/myapp`)
|
||||
* A resource base for static content (a document root)
|
||||
* A class loader to obtain classes specific to the context (typically from `/WEB-INF/classes` and `/WEB-INF/lib`)
|
||||
* Virtual host names
|
||||
|
||||
Contexts implementations include:
|
||||
|
||||
* `ContextHandler`
|
||||
* `ServletContextHandler`
|
||||
* `WebAppContext`
|
||||
|
||||
A web application context combines handlers for security, session and servlets in a single unit
|
||||
that you can configure with a `web.xml` descriptor.
|
||||
|
||||
==== Web Application
|
||||
|
||||
A `WebAppContext` is a derivation of `ServletContextHandler` that supports the standardized layout
|
||||
of a web application and configuration of session, security, listeners, filter, servlets, and JSP
|
||||
via a `web.xml` descriptor normally found in the `/WEB-INF` directory of a web application.
|
||||
|
||||
image:basic-architecture-web-application.png[image,width=576]
|
||||
|
||||
Essentially `WebAppContext` is a convenience class that assists the construction and configuration
|
||||
of other handlers to achieve a standard web application configuration.
|
||||
Configuration is actually done by pluggable implementations of the Configuration class and the
|
||||
prime among these is `WebXmlConfiguration.`
|
|
@ -1,99 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[creating-custom-protocol]]
|
||||
=== Creating a Custom Protocol
|
||||
|
||||
You can create custom protocols with Jetty. This page provides an example of how to do so, with Telnet as the protocol.
|
||||
|
||||
To create a custom Telnet protocol, complete the following tasks:
|
||||
|
||||
* Implement a `TelnetServerConnectionFactory`.
|
||||
* Implement a `TelnetServerConnection` by extending `o.e.j.io.AbstractConnection`.
|
||||
* Create a parser/interpreter for the bytes you receive (this is totally independent from Jetty).
|
||||
* If needed, design an API for the application to use to process the bytes received (also independent from Jetty).
|
||||
The API likely has a _respond back_ primitive that uses a Jetty provided `EndPoint` and `EndPoint.write(Callback, Buffer...)` to write the response bytes.
|
||||
|
||||
[[server-connection-factory]]
|
||||
==== Implementing a TelnetServerConnectionFactory
|
||||
|
||||
Begin with an `org.eclipse.jetty.server.ServerConnector`, which you can use as is. `ServerConnector` takes a `o.e.j.server.ConnectionFactory`, which creates `o.e.j.io.Connection` objects that interpret the bytes the connector receives.
|
||||
You must implement `ConnectionFactory` with a `TelnetServerConnectionFactory`, where you return a Connection implementation (for example, `TelnetServerConnection`).
|
||||
|
||||
[[telnet-server-connection]]
|
||||
==== Implementing the TelnetServerConnection
|
||||
|
||||
For the Connection implementation you need to extend from `o.e.j.io.AbstractConnection` because it provides many facilities that you would otherwise need to re-implement from scratch.
|
||||
|
||||
For each Connection instance there is associated an `o.e.j.io.EndPoint` instance.
|
||||
Think of `EndPoint` as a specialized version of JDK’s `SocketChannel`.
|
||||
You use the `EndPoint` to read, write, and close.
|
||||
You don’t need to implement `EndPoint`, because Jetty provides concrete
|
||||
classes for you to use.
|
||||
|
||||
The Connection is the _passive_ side (that is, Jetty calls it when there is data to read), while the `EndPoint` is the active part (that is, applications call it to write data to the other end).
|
||||
When there is data to read, Jetty calls `AbstractConnection.onFillable()`, which you must implement in your `TelnetServerConnection`.
|
||||
|
||||
A typical implementation reads bytes from the `EndPoint` by calling `EndPoint.fill(ByteBuffer)`.
|
||||
For examples, look at both the simpler `SPDYConnection` (in the SPDY client package, but server also uses it), and the slightly more complex `HttpConnection`.
|
||||
|
||||
[[parser-interpreter]]
|
||||
==== Parsing the Bytes Received
|
||||
|
||||
After you read the bytes, you need to parse them.
|
||||
For the Telnet protocol there is not much to parse, but perhaps you have your own commands that you want to interpret and execute.
|
||||
Therefore typically every connection has an associated parser instance.
|
||||
In turn, a parser usually emits parse events that a parser listener interprets, as the following examples illustrate:
|
||||
|
||||
* In HTTP, the Jetty HTTP parser parses the request line (and emits a parser event), then parses the headers (and emits a parser event for each) until it recognizes the end of the headers (and emits another parser event).
|
||||
At that point, the _interpreter_ or parser listener (which for HTTP is `o.e.j.server.HttpChannel`) has all the information necessary to build a `HttpServletRequest` object and can call the user code (the web application, that is, servlets/filters).
|
||||
* In SPDY, the Jetty SPDY parser parses a SPDY frame (and emits a parser event), and the parser listener (an instance of o.e.j.spdy.StandardSession) interprets the parser events and calls user code (application-provided listeners).
|
||||
|
||||
With `ConnectionFactory`, Connection, parser, and parser listeners in place, you have configured the read side.
|
||||
|
||||
[[api-byte-processor]]
|
||||
==== Designing an API to Process Bytes
|
||||
|
||||
At this point, server applications typically write data back to the client.
|
||||
|
||||
The Servlet API (for HTTP) or application-provided listeners (for SPDY) expose an interface to web applications so that they can write data back to the client.
|
||||
The implementation of those interfaces must link back to the `EndPoint` instance associated with the Connection instance so that it can write data via `EndPoint.write(Callback, ByteBuffer...)`.
|
||||
This is an asynchronous call, and it notifies the callback when all the buffers have been fully written.
|
||||
|
||||
For example, in the Servlet API, applications use a `ServletOutputStream` to write the response content.
|
||||
`ServletOutputStream` is an abstract class that Jetty implements, enabling Jetty to handle the writes from the web application; the writes eventually end up in an `EndPoint.write(...)` call.
|
||||
|
||||
[[api-tips]]
|
||||
===== Tips for Designing an API
|
||||
|
||||
If you want to write a completely asynchronous implementation, your API to write data to the client must have a callback/promise concept: “Call me back when you are done, and (possibly) give me the result of the computation."
|
||||
|
||||
SPDY’s Stream class is a typical example.
|
||||
Notice how the methods there exist in two versions, a synchronous (blocking) one, and an asynchronous one that takes as last parameter a Callback (if no result is needed), or a Promise (if a result is needed).
|
||||
It is trivial to write the synchronous version in terms of the asynchronous version.
|
||||
|
||||
You can use `EndPoint.write(Callback, ByteBuffer...)` in a blocking way as follows:
|
||||
|
||||
[source, java, subs="{sub-order}"]
|
||||
----
|
||||
FutureCallback callback = new FutureCallback();
|
||||
endPoint.write(callback, buffers);
|
||||
callback.get();
|
||||
----
|
||||
|
||||
With the snippet above your API can be synchronous or asynchronous (your choice), but implemented synchronously.
|
|
@ -1,166 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[framework-weld]]
|
||||
=== Weld
|
||||
|
||||
http://seamframework.org/Weld[Weld] can be used to add support for CDI (Contexts and Dependency Injection) to Servlets, Listeners and Filters.
|
||||
It is easily configured with Jetty 9.
|
||||
|
||||
[[weld-setup-distro]]
|
||||
==== Weld Setup
|
||||
|
||||
The easiest way to configure weld is within the Jetty distribution itself.
|
||||
This can be accomplished either by enabling one of the startup link:#startup-modules[modules] for Weld, or by creating/editing a `jetty-web.xml` descriptor (see also https://www.eclipse.org/jetty/documentation/current/jetty-web-xml-config.html[Jetty XML Reference]).
|
||||
|
||||
===== Jetty Weld Modules
|
||||
|
||||
====== Jetty `cdi-decorate` Module
|
||||
|
||||
Since Jetty 9.4.20 and Weld 3.1.2.Final, the Weld/Jetty integration uses the jetty `cdi-decorate` module.
|
||||
To activate this module in Jetty the `cdi-decorate` module needs activated on the command line, which can be done as follows:
|
||||
|
||||
-------------------------
|
||||
cd $JETTY_BASE
|
||||
java -jar $JETTY_HOME/start.jar --add-to-start=cdi-decorate
|
||||
-------------------------
|
||||
|
||||
====== Jetty `cdi2` Module
|
||||
|
||||
For versions prior to Jetty 9.4.20 and Weld 3.1.2, the Weld/Jetty integration required some internal Jetty APIs to be made visible to the web application.
|
||||
This can be done using the deprecated `cdi2` module either by activating the `cdi2` module:
|
||||
|
||||
-------------------------
|
||||
cd $JETTY_BASE
|
||||
java -jar $JETTY_HOME/start.jar --add-to-start=cdi2
|
||||
-------------------------
|
||||
|
||||
|
||||
===== Jetty-Web XML
|
||||
|
||||
|
||||
[source.XML, xml]
|
||||
-------------------------------------------------------------
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
|
||||
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
|
||||
<Call name="prependServerClass">
|
||||
<Arg>-org.eclipse.jetty.util.Decorator</Arg>
|
||||
</Call>
|
||||
<Call name="prependServerClass">
|
||||
<Arg>-org.eclipse.jetty.util.DecoratedObjectFactory</Arg>
|
||||
</Call>
|
||||
<Call name="prependServerClass">
|
||||
<Arg>-org.eclipse.jetty.server.handler.ContextHandler.</Arg>
|
||||
</Call>
|
||||
<Call name="prependServerClass">
|
||||
<Arg>-org.eclipse.jetty.server.handler.ContextHandler</Arg>
|
||||
</Call>
|
||||
<Call name="prependServerClass">
|
||||
<Arg>-org.eclipse.jetty.servlet.ServletContextHandler</Arg>
|
||||
</Call>
|
||||
</Configure>
|
||||
-------------------------------------------------------------
|
||||
|
||||
//TODO Fix for 10
|
||||
____
|
||||
[TIP]
|
||||
Directly modifying the web application classpath via `jetty-web.xml` will not work for Jetty 10.0.0 and later.
|
||||
____
|
||||
|
||||
===== Jetty `cdi-spi` Module
|
||||
Since Jetty 9.4.20 the Jetty `cdi-spi` module has been available that integrates any compliant CDI implementation by directly calling the CDI SPI.
|
||||
Since Weld support specific Jetty integration, it is not recommended to use this module with Weld.
|
||||
|
||||
When you start up your Jetty distribution with the webapp you should see output similar to the following (providing your logging is the default configuration):
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
....
|
||||
2015-06-18 12:13:54.924:INFO::main: Logging initialized @485ms
|
||||
2015-06-18 12:13:55.231:INFO:oejs.Server:main: jetty-{VERSION}
|
||||
2015-06-18 12:13:55.264:INFO:oejdp.ScanningAppProvider:main: Deployment monitor [file:///tmp/cdi-demo/webapps/] at interval 1
|
||||
2015-06-18 12:13:55.607:WARN:oeja.AnnotationConfiguration:main: ServletContainerInitializers: detected. Class hierarchy: empty
|
||||
Jun 18, 2015 12:13:55 PM org.jboss.weld.environment.servlet.EnhancedListener onStartup
|
||||
INFO: WELD-ENV-001008: Initialize Weld using ServletContainerInitializer
|
||||
Jun 18, 2015 12:13:55 PM org.jboss.weld.bootstrap.WeldStartup <clinit>
|
||||
INFO: WELD-000900: 2.2.9 (Final)
|
||||
Jun 18, 2015 12:13:55 PM org.jboss.weld.environment.servlet.deployment.WebAppBeanArchiveScanner scan
|
||||
WARN: WELD-ENV-001004: Found both WEB-INF/beans.xml and WEB-INF/classes/META-INF/beans.xml. It's not portable to use both locations at the same time. Weld is going to use file:/tmp/jetty-0.0.0.0-8080-cdi-webapp.war-_cdi-webapp-any-8161614308407422636.dir/webapp/WEB-INF/beans.xml.
|
||||
Jun 18, 2015 12:13:55 PM org.jboss.weld.bootstrap.WeldStartup startContainer
|
||||
INFO: WELD-000101: Transactional services not available. Injection of @Inject UserTransaction not available. Transactional observers will be invoked synchronously.
|
||||
Jun 18, 2015 12:13:55 PM org.jboss.weld.interceptor.util.InterceptionTypeRegistry <clinit>
|
||||
WARN: WELD-001700: Interceptor annotation class javax.ejb.PostActivate not found, interception based on it is not enabled
|
||||
Jun 18, 2015 12:13:55 PM org.jboss.weld.interceptor.util.InterceptionTypeRegistry <clinit>
|
||||
WARN: WELD-001700: Interceptor annotation class javax.ejb.PrePassivate not found, interception based on it is not enabled
|
||||
Jun 18, 2015 12:13:56 PM org.jboss.weld.bootstrap.MissingDependenciesRegistry handleResourceLoadingException
|
||||
Jun 18, 2015 12:13:56 PM org.jboss.weld.environment.servlet.WeldServletLifecycle findContainer
|
||||
INFO: WELD-ENV-001002: Container detection skipped - custom container class loaded: org.jboss.weld.environment.jetty.JettyContainer.
|
||||
Jun 18, 2015 12:13:56 PM org.jboss.weld.environment.jetty.JettyContainer initialize
|
||||
INFO: WELD-ENV-001200: Jetty 7.2+ detected, CDI injection will be available in Servlets and Filters. Injection into Listeners should work on Jetty 9.1.1 and newer.
|
||||
Jun 18, 2015 12:13:56 PM org.jboss.weld.environment.servlet.Listener contextInitialized
|
||||
INFO: WELD-ENV-001006: org.jboss.weld.environment.servlet.EnhancedListener used for ServletContext notifications
|
||||
Jun 18, 2015 12:13:56 PM org.jboss.weld.environment.servlet.EnhancedListener contextInitialized
|
||||
INFO: WELD-ENV-001009: org.jboss.weld.environment.servlet.Listener used for ServletRequest and HttpSession notifications
|
||||
2015-06-18 12:13:56.535:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@6574b225{/cdi-webapp,file:///tmp/jetty-0.0.0.0-8080-cdi-webapp.war-_cdi-webapp-any-8161614308407422636.dir/webapp/,AVAILABLE}{/cdi-webapp.war}
|
||||
2015-06-18 12:13:56.554:INFO:oejs.ServerConnector:main: Started ServerConnector@7112f81c{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
|
||||
2015-06-18 12:13:56.587:INFO:oejus.SslContextFactory:main: x509={jetty.eclipse.org=jetty} wild={} alias=null for SslContextFactory@3214ee6(file:///tmp/cdi-demo/etc/keystore,file:///tmp/cdi-demo/etc/keystore)
|
||||
2015-06-18 12:13:56.821:INFO:oejs.ServerConnector:main: Started ServerConnector@69176a9b{SSL,[ssl, http/1.1]}{0.0.0.0:8443}
|
||||
2015-06-18 12:13:56.822:INFO:oejs.Server:main: Started @2383ms
|
||||
|
||||
....
|
||||
|
||||
For use with the jetty-maven-plugin, the best idea is to make the org.jboss.weld.servlet:weld-servlet artifact a _plugin_ dependency (__not__ a webapp dependency).
|
||||
|
||||
[[weld-embedded]]
|
||||
==== Embedded Jetty
|
||||
|
||||
When starting embedded Jetty programmatically from the `main` method it is necessary to register Weld's listener:
|
||||
|
||||
[source.JAVA, java]
|
||||
----
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Server jetty = new Server(8080);
|
||||
WebAppContext context = new WebAppContext();
|
||||
context.setContextPath("/");
|
||||
context.setResourceBase("src/main/resources");
|
||||
jetty.setHandler(context);
|
||||
context.addServlet(HelloWorldServlet.class, "/*");
|
||||
|
||||
context.addEventListener(new DecoratingListener()); # <1>
|
||||
context.addEventListener(new Listener()); # <2>
|
||||
|
||||
jetty.start();
|
||||
jetty.join();
|
||||
}
|
||||
|
||||
public static class HelloWorldServlet extends HttpServlet {
|
||||
|
||||
@Inject BeanManager manager;
|
||||
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
resp.setContentType("text/plain");
|
||||
resp.getWriter().append("Hello from " + manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<1> Jetty's `org.eclipse.jetty.webapp.DecoratingListener` registered programmatically (since Jetty-9.4.20)
|
||||
<2> Weld's `org.jboss.weld.environment.servlet.Listener` registered programmatically
|
||||
----
|
|
@ -16,19 +16,20 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
:doctitle: Eclipse Jetty: Distribution Guide
|
||||
:doctitle: Eclipse Jetty: Operations Guide
|
||||
:author: Jetty Developers
|
||||
:email: jetty-dev@eclipse.org
|
||||
:revnumber: 1.0
|
||||
:revnumber: {version}
|
||||
:revdate: {TIMESTAMP}
|
||||
:toc: left
|
||||
:toc-title: Distribution Guide
|
||||
:toc-title: Operations Guide
|
||||
:toc-style:
|
||||
|
||||
:header-style: eclipse-thin
|
||||
:breadcrumb-style: eclipse-thin
|
||||
:footer-style: default
|
||||
:breadcrumb: Home:../index.html | Distribution Guide:./index.html
|
||||
|
||||
:breadcrumb: Home:../index.html | Operations Guide:./index.html
|
||||
|
||||
// docinfo lets you pull in shared content and/or influence via render type
|
||||
//:docinfodir: {DOCINFODIR}/documentation
|
||||
|
@ -55,21 +56,35 @@ endif::[]
|
|||
// suppress automatic id generation
|
||||
:sectids!:
|
||||
|
||||
// download and install
|
||||
include::introduction.adoc[]
|
||||
// TODO: jetty.home vs jetty.base
|
||||
|
||||
include::startup/chapter.adoc[]
|
||||
include::contexts/chapter.adoc[]
|
||||
include::deploying/chapter.adoc[]
|
||||
include::connectors/chapter.adoc[]
|
||||
include::http2/chapter.adoc[]
|
||||
include::logging/chapter.adoc[]
|
||||
include::security/chapter.adoc[]
|
||||
include::sessions/chapter.adoc[]
|
||||
include::jsp/chapter.adoc[]
|
||||
include::annotations/chapter.adoc[]
|
||||
include::jmx/chapter.adoc[]
|
||||
include::jndi/chapter.adoc[]
|
||||
include::alpn/chapter.adoc[]
|
||||
include::fastcgi/chapter.adoc[]
|
||||
include::extras/chapter.adoc[]
|
||||
include::runner/chapter.adoc[]
|
||||
include::tuning/chapter.adoc[]
|
||||
// TODO: how jetty standalone work what start.jar does, command line options, modules, etc.
|
||||
// TODO: this is mostly in old_docs/startup.
|
||||
// TODO: [[quick-start-configure]]
|
||||
|
||||
// TODO: list of modules, how to write a custom module, examples e.g. http2, etc.
|
||||
|
||||
== OLD DOCUMENTATION
|
||||
|
||||
include::old_docs/gettingstarted/introduction/chapter.adoc[]
|
||||
include::old_docs/gettingstarted/getting-started/chapter.adoc[]
|
||||
// TODO: how about other directories under old_docs/gettingstarted/?
|
||||
include::old_docs/startup/chapter.adoc[]
|
||||
include::old_docs/contexts/chapter.adoc[]
|
||||
include::old_docs/deploying/chapter.adoc[]
|
||||
include::old_docs/connectors/chapter.adoc[]
|
||||
include::old_docs/http2/chapter.adoc[]
|
||||
include::old_docs/logging/chapter.adoc[]
|
||||
include::old_docs/security/chapter.adoc[]
|
||||
include::old_docs/sessions/sessions.adoc[]
|
||||
include::old_docs/jsp/chapter.adoc[]
|
||||
include::old_docs/annotations/chapter.adoc[]
|
||||
include::old_docs/jmx/chapter.adoc[]
|
||||
include::old_docs/jndi/chapter.adoc[]
|
||||
include::old_docs/alpn/chapter.adoc[]
|
||||
include::old_docs/fastcgi/chapter.adoc[]
|
||||
include::old_docs/extras/chapter.adoc[]
|
||||
include::old_docs/runner/chapter.adoc[]
|
||||
include::old_docs/tuning/chapter.adoc[]
|
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under
|
||||
// the terms of the Eclipse Public License 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0
|
||||
//
|
||||
// This Source Code may also be made available under the following
|
||||
// Secondary Licenses when the conditions for such availability set
|
||||
// forth in the Eclipse Public License, v. 2.0 are satisfied:
|
||||
// the Apache License v2.0 which is available at
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
[[og-guide]]
|
||||
== Eclipse Jetty Operations Guide
|
||||
|
||||
The Eclipse Jetty Operations Guide targets sysops, devops, and developers who want to install Eclipse Jetty as a standalone server to deploy web applications.
|
||||
|
||||
[[og-download]]
|
||||
=== Downloading Eclipse Jetty
|
||||
|
||||
The Eclipse Jetty distribution is available for download from link:https://www.eclipse.org/jetty/download.html[]
|
||||
|
||||
The Eclipse Jetty distribution is available in both `zip` and `gzip` formats; download the one most appropriate for your system, typically `zip` for Windows and `gzip` for other operative systems.
|
||||
|
||||
[[og-install]]
|
||||
=== Installing Eclipse Jetty
|
||||
|
||||
After the download, unpacking the Eclipse Jetty distribution will extract the files into a directory called `jetty-distribution-VERSION`, where `VERSION` is the version that you downloaded, for example `10.0.0`, so that the directory is called `jetty-distribution-10.0.0`.
|
||||
|
||||
Unpack the Eclipse Jetty distribution compressed file in a convenient location, for example under `/opt`.
|
||||
|
||||
The rest of the instructions in this documentation will refer to this location as `$JETTY_HOME`.
|
||||
|
||||
IMPORTANT: It is important that *only* stable release versions are used in production environments.
|
||||
Versions that have been deprecated or are released as Milestones (M), Alpha, Beta or Release Candidates (RC) are *not* suitable for production as they may contain security flaws or incomplete/non-functioning feature sets.
|
||||
|
||||
[[og-running]]
|
||||
=== Running Eclipse Jetty
|
||||
|
||||
Eclipse Jetty as a standalone server has no graphical user interface, so configuring and running the server is done from the command line.
|
||||
|
||||
TODO: section about general architecture - modules etc.
|
|
@ -34,9 +34,9 @@ Additional settings, including construction your own constructor Jetty XML files
|
|||
Out of the box, Jetty provides several link:#startup-modules[modules] for enabling different types of connectors, from HTTP to HTTPS, HTTP/2, and others.
|
||||
If you startup Jetty with the `--list-modules=connector` command, you can see a list of all available connector modules:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
[source,screen,subs="{sub-order}"]
|
||||
....
|
||||
[mybase]$ java -jar $JETTY_HOME/start.jar --list-modules=connector
|
||||
[my-base]$ java -jar /path/to/jetty-home/start.jar --list-modules=connector
|
||||
|
||||
Available Modules:
|
||||
==================
|
||||
|
@ -276,9 +276,9 @@ jetty.http.port=5231
|
|||
|
||||
Now when the server is started, HTTP connections will enter on port 5231:
|
||||
|
||||
[source, screen, subs="{sub-order}"]
|
||||
[source,screen,subs="{sub-order}"]
|
||||
....
|
||||
[mybase] java -jar ../start.jar
|
||||
[my-base]$ java -jar /path/to/jetty-home/start.jar
|
||||
2017-08-31 10:31:32.955:INFO::main: Logging initialized @366ms to org.eclipse.jetty.util.log.StdErrLog
|
||||
2017-08-31 10:31:33.109:INFO:oejs.Server:main: jetty-{VERSION}
|
||||
2017-08-31 10:31:33.146:INFO:oejs.AbstractConnector:main: Started ServerConnector@2ef9b8bc{HTTP/1.1,[http/1.1]}{0.0.0.0:5231}
|
|
@ -63,10 +63,10 @@ jetty.sslContext.keyStorePassword::
|
|||
|
||||
Enabling Conscrypt SSL is just as easy as default SSL - enable both the `conscrypt` and `ssl` link:#startup-modules[modules]:
|
||||
|
||||
[source, plain, subs="{sub-order}"]
|
||||
[source,plain,subs="{sub-order}"]
|
||||
----
|
||||
$ cd ${JETTY_HOME}
|
||||
$ java -jar ../start.jar --add-to-start=ssl,conscrypt
|
||||
$ cd ${JETTY_BASE}
|
||||
$ java -jar /path/to/jetty-home/start.jar --add-to-start=ssl,conscrypt
|
||||
|
||||
ALERT: There are enabled module(s) with licenses.
|
||||
The following 1 module(s):
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue