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