From 33eccf82dcce704854f3f1e4edac7d3f6a0513fe Mon Sep 17 00:00:00 2001 From: olivier lamy Date: Wed, 21 Nov 2018 15:56:39 +1000 Subject: [PATCH 01/10] remove non used test-dispatch-webapp Signed-off-by: olivier lamy --- .../test-webapps/test-dispatch-webapp/pom.xml | 49 ------- .../main/java/com/acme/DispatchServlet.java | 123 ------------------ .../src/main/webapp/WEB-INF/web.xml | 24 ---- .../src/main/webapp/images/jetty_banner.gif | Bin 72262 -> 0 bytes .../main/webapp/images/small_powered_by.gif | Bin 4787 -> 0 bytes .../src/main/webapp/index.html | 48 ------- .../src/main/webapp/stylesheet.css | 7 - 7 files changed, 251 deletions(-) delete mode 100644 tests/test-webapps/test-dispatch-webapp/pom.xml delete mode 100644 tests/test-webapps/test-dispatch-webapp/src/main/java/com/acme/DispatchServlet.java delete mode 100644 tests/test-webapps/test-dispatch-webapp/src/main/webapp/WEB-INF/web.xml delete mode 100644 tests/test-webapps/test-dispatch-webapp/src/main/webapp/images/jetty_banner.gif delete mode 100644 tests/test-webapps/test-dispatch-webapp/src/main/webapp/images/small_powered_by.gif delete mode 100644 tests/test-webapps/test-dispatch-webapp/src/main/webapp/index.html delete mode 100644 tests/test-webapps/test-dispatch-webapp/src/main/webapp/stylesheet.css diff --git a/tests/test-webapps/test-dispatch-webapp/pom.xml b/tests/test-webapps/test-dispatch-webapp/pom.xml deleted file mode 100644 index 5afe024043c..00000000000 --- a/tests/test-webapps/test-dispatch-webapp/pom.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - 4.0.0 - - org.eclipse.jetty.tests - test-webapps-parent - 9.3.0-SNAPSHOT - - Jetty Tests :: Webapps :: Dispatch Webapp - test-dispatch-webapp - war - - ${project.groupId}.dispatch - - - - - org.apache.maven.plugins - maven-deploy-plugin - - true - - - - org.eclipse.jetty - jetty-maven-plugin - ${project.version} - - 1 - true - - src/main/webapp - src/main/webapp/WEB-INF/web.xml - /test-dispatch - .*/javax.servlet-[^/]*\.jar$ - true - - - - - - - - javax.servlet - javax.servlet-api - provided - - - diff --git a/tests/test-webapps/test-dispatch-webapp/src/main/java/com/acme/DispatchServlet.java b/tests/test-webapps/test-dispatch-webapp/src/main/java/com/acme/DispatchServlet.java deleted file mode 100644 index 257780be849..00000000000 --- a/tests/test-webapps/test-dispatch-webapp/src/main/java/com/acme/DispatchServlet.java +++ /dev/null @@ -1,123 +0,0 @@ -// -// ======================================================================== -// Copyright (c) 1995-2018 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 com.acme; - -import static javax.servlet.RequestDispatcher.FORWARD_PATH_INFO; -import static javax.servlet.RequestDispatcher.FORWARD_QUERY_STRING; -import static javax.servlet.RequestDispatcher.FORWARD_SERVLET_PATH; -import static javax.servlet.RequestDispatcher.INCLUDE_PATH_INFO; -import static javax.servlet.RequestDispatcher.INCLUDE_QUERY_STRING; -import static javax.servlet.RequestDispatcher.INCLUDE_REQUEST_URI; -import static javax.servlet.RequestDispatcher.INCLUDE_SERVLET_PATH; - -import java.io.IOException; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - - -public class DispatchServlet extends HttpServlet -{ - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException - { - doGet(req, resp); - } - - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException - { - Integer depth = (Integer)request.getAttribute("depth"); - if (depth==null) - depth=1; - else - depth=depth+1; - request.setAttribute("depth", depth); - - String path=request.getServletPath(); - String info=request.getPathInfo(); - String query=request.getQueryString(); - - boolean include=request.getAttribute(INCLUDE_REQUEST_URI)!=null; - - String tpath = include?(String)request.getAttribute(INCLUDE_SERVLET_PATH):path; - String tinfo = include?(String)request.getAttribute(INCLUDE_PATH_INFO):info; - - if ("/forward".equals(tpath)) - { - getServletContext().getRequestDispatcher(tinfo+"?depth="+depth+"&p"+depth+"="+"ABCDEFGHI".charAt(depth)).forward(request, response); - } - else if ("/include".equals(tpath)) - { - response.setContentType("text/html"); - PrintWriter out = response.getWriter(); - out.println("

Dispatch Depth="+depth+"

");
-            out.printf("            %30s%30s%30s%n","REQUEST","FORWARD","INCLUDE");
-            out.printf("servletPath:%30s%30s%30s%n",path,request.getAttribute(FORWARD_SERVLET_PATH),request.getAttribute(INCLUDE_SERVLET_PATH));
-            out.printf("   pathInfo:%30s%30s%30s%n",info,request.getAttribute(FORWARD_PATH_INFO),request.getAttribute(INCLUDE_PATH_INFO));
-            out.printf("      query:%30s%30s%30s%n",query,request.getAttribute(FORWARD_QUERY_STRING),request.getAttribute(INCLUDE_QUERY_STRING));
-            out.println();
-            printParameters(out, request.getParameterMap());
-            out.println("
"); - out.println("
"); - getServletContext().getRequestDispatcher(tinfo+"?depth="+depth+"&p"+depth+"="+"BCDEFGHI".charAt(depth)).include(request, response); - out.println("
"); - out.println("

Dispatch Depth="+depth+"

");
-            out.printf("            %30s%30s%30s%n","REQUEST","FORWARD","INCLUDE");
-            out.printf("servletPath:%30s%30s%30s%n",path,request.getAttribute(FORWARD_SERVLET_PATH),request.getAttribute(INCLUDE_SERVLET_PATH));
-            out.printf("   pathInfo:%30s%30s%30s%n",info,request.getAttribute(FORWARD_PATH_INFO),request.getAttribute(INCLUDE_PATH_INFO));
-            out.printf("      query:%30s%30s%30s%n",query,request.getAttribute(FORWARD_QUERY_STRING),request.getAttribute(INCLUDE_QUERY_STRING));
-            out.println();
-            printParameters(out, request.getParameterMap());
-            out.println("
"); - } - else - { - response.setContentType("text/html"); - PrintWriter out = response.getWriter(); - out.println("

Dispatch Depth="+depth+"

");
-            out.printf("            %30s%30s%30s%n","REQUEST","FORWARD","INCLUDE");
-            out.printf("servletPath:%30s%30s%30s%n",path,request.getAttribute(FORWARD_SERVLET_PATH),request.getAttribute(INCLUDE_SERVLET_PATH));
-            out.printf("   pathInfo:%30s%30s%30s%n",info,request.getAttribute(FORWARD_PATH_INFO),request.getAttribute(INCLUDE_PATH_INFO));
-            out.printf("      query:%30s%30s%30s%n",query,request.getAttribute(FORWARD_QUERY_STRING),request.getAttribute(INCLUDE_QUERY_STRING));
-            out.println();
-            printParameters(out, request.getParameterMap());
-            out.println("
"); - } - } - - private static void printParameters(PrintWriter out, Map params) - { - List names = new ArrayList<>(params.keySet()); - Collections.sort(names); - - for (String name: names) - out.printf("%10s : %s%n", name,Arrays.asList(params.get(name))); - } - -} diff --git a/tests/test-webapps/test-dispatch-webapp/src/main/webapp/WEB-INF/web.xml b/tests/test-webapps/test-dispatch-webapp/src/main/webapp/WEB-INF/web.xml deleted file mode 100644 index 77c8f0c19fb..00000000000 --- a/tests/test-webapps/test-dispatch-webapp/src/main/webapp/WEB-INF/web.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - Test Dispatch WebApp - - - Dispatch - com.acme.DispatchServlet - 2 - - - - Dispatch - /forward/* - /include/* - /info/* - - - diff --git a/tests/test-webapps/test-dispatch-webapp/src/main/webapp/images/jetty_banner.gif b/tests/test-webapps/test-dispatch-webapp/src/main/webapp/images/jetty_banner.gif deleted file mode 100644 index 0a9b1e019bafbfd2c80f59898ca602c32edc79fa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 72262 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ℑ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 - - Test Dispatch WebApp - - - - - - - -

Request Dispatching Query Tests

-

-

-

Request Dispatching Form POST Tests

-

    -
  • -
  • -
  • -
  • -
  • -
  • -
-

-

Request Dispatching Query Form POST Tests

-

    -
  • -
  • -
  • -
  • -
  • -
  • -
-

- -
-
- -
- - - diff --git a/tests/test-webapps/test-dispatch-webapp/src/main/webapp/stylesheet.css b/tests/test-webapps/test-dispatch-webapp/src/main/webapp/stylesheet.css deleted file mode 100644 index f90463dd2c9..00000000000 --- a/tests/test-webapps/test-dispatch-webapp/src/main/webapp/stylesheet.css +++ /dev/null @@ -1,7 +0,0 @@ -body {color: #2E2E2E; font-family:sans-serif; font-size:90%;} -h1 {font-variant: small-caps; font-size:130%; letter-spacing: 0.1em;} -h2 {font-variant: small-caps; font-size:100%; letter-spacing: 0.1em; margin-top:2em;} -h3 {font-size:100%; letter-spacing: 0.1em;} - -span.pass { color: green; } -span.fail { color:red; } From 14c2431b1ddc5f45b387a67b14d9922dc6622594 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Thu, 22 Nov 2018 12:46:11 +0100 Subject: [PATCH 02/10] Updated Conscrypt to 1.4.1. Signed-off-by: Simone Bordet --- jetty-osgi/test-jetty-osgi/pom.xml | 2 +- pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/jetty-osgi/test-jetty-osgi/pom.xml b/jetty-osgi/test-jetty-osgi/pom.xml index 408a154ea33..f5230342c47 100644 --- a/jetty-osgi/test-jetty-osgi/pom.xml +++ b/jetty-osgi/test-jetty-osgi/pom.xml @@ -573,7 +573,7 @@ org.conscrypt conscrypt-openjdk-uber - 1.0.0.RC11 + ${conscrypt.version} test diff --git a/pom.xml b/pom.xml index 0aeca264a13..6964d749aa7 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ 8.5.33.1 undefined - 1.1.4 + 1.4.1 7.0 1.21 benchmarks From fc0d2569ef8fae0b298938422af2cb1b5dff8866 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 22 Nov 2018 18:08:46 +0100 Subject: [PATCH 03/10] Issue #3132 improve dump readability Signed-off-by: Greg Wilkins --- .../jetty/util/component/Dumpable.java | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java index eeee4dd9e74..ff9a993e269 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java @@ -100,7 +100,7 @@ public interface Dumpable } catch (Throwable th) { - out.append("=>").append(th.toString()).append("\n"); + out.append("=> ").append(th.toString()).append("\n"); } } @@ -134,12 +134,12 @@ public interface Dumpable for (Iterator i = container.getBeans().iterator(); i.hasNext();) { Object bean = i.next(); - String nextIndent = indent + ((i.hasNext() || size>0) ? "| " : " "); + String nextIndent = indent + ((i.hasNext() || size>0) ? "| " : " "); if (bean instanceof LifeCycle) { if (container.isManaged(bean)) { - out.append(indent).append("+="); + out.append(indent).append("+= "); if (bean instanceof Dumpable) ((Dumpable)bean).dump(out,nextIndent); else @@ -147,7 +147,7 @@ public interface Dumpable } else if (containerLifeCycle != null && containerLifeCycle.isAuto(bean)) { - out.append(indent).append("+?"); + out.append(indent).append("+? "); if (bean instanceof Dumpable) ((Dumpable)bean).dump(out,nextIndent); else @@ -155,18 +155,18 @@ public interface Dumpable } else { - out.append(indent).append("+~"); + out.append(indent).append("+~ "); dumpObject(out, bean); } } else if (containerLifeCycle != null && containerLifeCycle.isUnmanaged(bean)) { - out.append(indent).append("+~"); + out.append(indent).append("+~ "); dumpObject(out, bean); } else { - out.append(indent).append("+-"); + out.append(indent).append("+- "); if (bean instanceof Dumpable) ((Dumpable)bean).dump(out,nextIndent); else @@ -179,8 +179,8 @@ public interface Dumpable for (Iterator i = ((Iterable)object).iterator(); i.hasNext();) { Object item = i.next(); - String nextIndent = indent + ((i.hasNext() || size>0) ? "| " : " "); - out.append(indent).append("+:"); + String nextIndent = indent + ((i.hasNext() || size>0) ? "| " : " "); + out.append(indent).append("+: "); if (item instanceof Dumpable) ((Dumpable)item).dump(out,nextIndent); else @@ -192,8 +192,8 @@ public interface Dumpable for (Iterator> i = ((Map)object).entrySet().iterator(); i.hasNext();) { Map.Entry entry = i.next(); - String nextIndent = indent + ((i.hasNext() || size>0) ? "| " : " "); - out.append(indent).append("+@").append(String.valueOf(entry.getKey())).append('='); + String nextIndent = indent + ((i.hasNext() || size>0) ? "| " : " "); + out.append(indent).append("+@ ").append(String.valueOf(entry.getKey())).append('='); Object item = entry.getValue(); if (item instanceof Dumpable) ((Dumpable)item).dump(out,nextIndent); @@ -209,13 +209,12 @@ public interface Dumpable for (Object item : extraChildren) { i++; - String nextIndent = indent + (i"); + String nextIndent = indent + (i "); if (item instanceof Dumpable) ((Dumpable)item).dump(out,nextIndent); else dumpObjects(out, nextIndent, item); } } - } From 54319589a496d260311748a1025300ac0b2019de Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Fri, 23 Nov 2018 16:13:30 +0100 Subject: [PATCH 04/10] Fix conscrypt version for osgi --- jetty-osgi/test-jetty-osgi/pom.xml | 1 + .../jetty/osgi/test/TestJettyOSGiBootHTTP2Conscrypt.java | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/jetty-osgi/test-jetty-osgi/pom.xml b/jetty-osgi/test-jetty-osgi/pom.xml index f5230342c47..e90e480bd90 100644 --- a/jetty-osgi/test-jetty-osgi/pom.xml +++ b/jetty-osgi/test-jetty-osgi/pom.xml @@ -601,6 +601,7 @@ **/TestJettyOSGiBootHTTP2 + -Dconscrypt-version=${conscrypt.version} diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootHTTP2Conscrypt.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootHTTP2Conscrypt.java index 721279f44ee..e461d6ab975 100644 --- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootHTTP2Conscrypt.java +++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootHTTP2Conscrypt.java @@ -100,9 +100,9 @@ public class TestJettyOSGiBootHTTP2Conscrypt res.add(CoreOptions.systemProperty("jetty.alpn.protocols").value("h2,http/1.1")); res.add(CoreOptions.systemProperty("jetty.sslContext.provider").value("Conscrypt")); - res.add(wrappedBundle(mavenBundle().groupId("org.conscrypt").artifactId("conscrypt-openjdk-uber").version("1.1.4")) + res.add(wrappedBundle(mavenBundle().groupId("org.conscrypt").artifactId("conscrypt-openjdk-uber").versionAsInProject()) .imports("javax.net.ssl,*") - .exports("org.conscrypt;version=1.1.4") + .exports("org.conscrypt;version="+System.getProperty("conscrypt-version")) .instructions("Bundle-NativeCode=META-INF/native/libconscrypt_openjdk_jni-linux-x86_64.so") .start()); res.add(mavenBundle().groupId("org.eclipse.jetty.osgi").artifactId("jetty-osgi-alpn").versionAsInProject().noStart()); From 4e672c6b27eeecbb7e1152453d1364ad948b51cf Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Fri, 23 Nov 2018 16:15:27 +0100 Subject: [PATCH 05/10] Issue #2646 - Better handle concurrent calls to change session id and invalidate within a context (#2670) * Issue #2646 handle concurrent invalidate/changeid calls Signed-off-by: Jan Bartel --- .../server/session/AbstractSessionCache.java | 39 +- .../eclipse/jetty/server/session/Session.java | 669 +++++++++++------- .../jetty/server/session/SessionCache.java | 27 +- .../jetty/server/session/SessionHandler.java | 132 +++- .../session/DefaultSessionCacheTest.java | 98 ++- 5 files changed, 649 insertions(+), 316 deletions(-) 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 c7c49a63bd0..1f22ba2e2c4 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 @@ -151,8 +151,9 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements /** * Remove the session with this identity from the store + * * @param id the id - * @return true if removed false otherwise + * @return Session that was removed or null */ public abstract Session doDelete (String id); @@ -727,12 +728,8 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements - - /** - * @see org.eclipse.jetty.server.session.SessionCache#renewSessionId(java.lang.String, java.lang.String) - */ @Override - public Session renewSessionId (String oldId, String newId) + public Session renewSessionId (String oldId, String newId, String oldExtendedId, String newExtendedId) throws Exception { if (StringUtil.isBlank(oldId)) @@ -741,17 +738,40 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements throw new IllegalArgumentException ("New session id is null"); Session session = get(oldId); - if (session == null) - return null; + renewSessionId(session, newId, newExtendedId); + return session; + } + + + /** + * Swap the id on a session. + * + * @param session the session for which to do the swap + * @param newId the new id + * @param newExtendedId the full id plus node id + * + * @throws Exception if there was a failure saving the change + */ + protected void renewSessionId (Session session, String newId, String newExtendedId) + throws Exception + { + if (session == null) + return; + try (Lock lock = session.lock()) { + String oldId = session.getId(); session.checkValidForWrite(); //can't change id on invalid session session.getSessionData().setId(newId); session.getSessionData().setLastSaved(0); //pretend that the session has never been saved before to get a full save - session.getSessionData().setDirty(true); //ensure we will try to write the session out + session.getSessionData().setDirty(true); //ensure we will try to write the session out + session.setExtendedId(newExtendedId); //remember the new extended id + session.setIdChanged(true); //session id changed + doPutIfAbsent(newId, session); //put the new id into our map doDelete (oldId); //take old out of map + if (_sessionDataStore != null) { _sessionDataStore.delete(oldId); //delete the session data with the old id @@ -759,7 +779,6 @@ public abstract class AbstractSessionCache extends ContainerLifeCycle implements } if (LOG.isDebugEnabled()) LOG.debug ("Session id {} swapped for new id {}", oldId, newId); - return session; } } 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 19268e88155..673b400ecd4 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 @@ -25,6 +25,7 @@ import java.util.Enumeration; import java.util.Iterator; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; @@ -41,66 +42,73 @@ import org.eclipse.jetty.util.thread.Locker; import org.eclipse.jetty.util.thread.Locker.Lock; - - /** * Session * * A heavy-weight Session object representing a HttpSession. Session objects - * relating to a context are kept in a {@link SessionCache}. The purpose of - * the SessionCache is to keep the working set of Session objects in memory - * so that they may be accessed quickly, and facilitate the sharing of a - * Session object amongst multiple simultaneous requests referring to the - * same session id. + * relating to a context are kept in a {@link SessionCache}. The purpose of the + * SessionCache is to keep the working set of Session objects in memory so that + * they may be accessed quickly, and facilitate the sharing of a Session object + * amongst multiple simultaneous requests referring to the same session id. * - * The {@link SessionHandler} coordinates - * the lifecycle of Session objects with the help of the SessionCache. + * The {@link SessionHandler} coordinates the lifecycle of Session objects with + * the help of the SessionCache. * * @see SessionHandler * @see org.eclipse.jetty.server.SessionIdManager */ public class Session implements SessionHandler.SessionIf { - private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session"); - - + private final static Logger LOG = Log.getLogger("org.eclipse.jetty.server.session"); + /** * */ - public final static String SESSION_CREATED_SECURE="org.eclipse.jetty.security.sessionCreatedSecure"; - - + public final static String SESSION_CREATED_SECURE = "org.eclipse.jetty.security.sessionCreatedSecure"; + /** * State * * Validity states of a session */ - public enum State {VALID, INVALID, INVALIDATING}; - - - + public enum State + { + VALID, INVALID, INVALIDATING, CHANGING + }; - - protected final SessionData _sessionData; //the actual data associated with a session - protected final SessionHandler _handler; //the manager of the session - protected String _extendedId; //the _id plus the worker name + public enum IdState + { + SET, CHANGING + }; + + protected final SessionData _sessionData; // the actual data associated with + // a session + + protected final SessionHandler _handler; // the manager of the session + + protected String _extendedId; // the _id plus the worker name protected long _requests; - protected boolean _idChanged; + + protected boolean _idChanged; + protected boolean _newSession; - protected State _state = State.VALID; //state of the session:valid,invalid or being invalidated - protected Locker _lock = new Locker(); //sync lock + + protected State _state = State.VALID; // state of the session:valid,invalid + // or being invalidated + + protected Locker _lock = new Locker(); // sync lock + protected Condition _stateChangeCompleted = _lock.newCondition(); protected boolean _resident = false; protected final SessionInactivityTimer _sessionInactivityTimer; - - + /* ------------------------------------------------------------- */ /** * SessionInactivityTimer * - * Each Session has a timer associated with it that fires whenever - * it has been idle (ie not accessed by a request) for a - * configurable amount of time, or the Session expires. + * Each Session has a timer associated with it that fires whenever it has + * been idle (ie not accessed by a request) for a configurable amount of + * time, or the Session expires. * * @see SessionCache * @@ -109,62 +117,65 @@ public class Session implements SessionHandler.SessionIf { protected final CyclicTimeout _timer; protected long _msec = -1; - + public SessionInactivityTimer() { _timer = new CyclicTimeout((getSessionHandler().getScheduler())) - { - @Override - public void onTimeoutExpired() - { - if (LOG.isDebugEnabled()) LOG.debug("Timer expired for session {}", getId()); - getSessionHandler().sessionInactivityTimerExpired(Session.this); - } - - }; + { + @Override + public void onTimeoutExpired() + { + if (LOG.isDebugEnabled()) + LOG.debug("Timer expired for session {}", getId()); + getSessionHandler().sessionInactivityTimerExpired(Session.this); + } + + }; } /** - * @param ms the timeout to set; -1 means that the timer will not be scheduled + * @param ms the timeout to set; -1 means that the timer will not be + * scheduled */ public void setTimeout(long ms) { _msec = ms; - if (LOG.isDebugEnabled()) LOG.debug("Session {} timer={}ms",getId(), ms); + if (LOG.isDebugEnabled()) + LOG.debug("Session {} timer={}ms", getId(), ms); } - - public void schedule () + public void schedule() { if (_msec > 0) { - if (LOG.isDebugEnabled()) LOG.debug("(Re)starting timer for session {} at {}ms",getId(), _msec); + if (LOG.isDebugEnabled()) + LOG.debug("(Re)starting timer for session {} at {}ms", getId(), _msec); _timer.schedule(_msec, TimeUnit.MILLISECONDS); } else { - if (LOG.isDebugEnabled()) LOG.debug("Not starting timer for session {}",getId()); + if (LOG.isDebugEnabled()) + LOG.debug("Not starting timer for session {}", getId()); } } - - + public void cancel() { _timer.cancel(); - if (LOG.isDebugEnabled()) LOG.debug("Cancelled timer for session {}",getId()); + if (LOG.isDebugEnabled()) + LOG.debug("Cancelled timer for session {}", getId()); } - - - public void destroy () + + public void destroy() { _timer.destroy(); - if (LOG.isDebugEnabled()) LOG.debug("Destroyed timer for session {}",getId()); + if (LOG.isDebugEnabled()) + LOG.debug("Destroyed timer for session {}", getId()); } } - /* ------------------------------------------------------------- */ /** * Create a new session @@ -172,17 +183,17 @@ public class Session implements SessionHandler.SessionIf * @param request the request the session should be based on * @param data the session data */ - public Session (SessionHandler handler, HttpServletRequest request, SessionData data) + public Session(SessionHandler handler, HttpServletRequest request, SessionData data) { _handler = handler; _sessionData = data; _newSession = true; _sessionData.setDirty(true); - _requests = 1; //access will not be called on this new session, but we are obviously in a request + _requests = 1; // access will not be called on this new session, but we + // are obviously in a request _sessionInactivityTimer = new SessionInactivityTimer(); } - - + /* ------------------------------------------------------------- */ /** @@ -191,19 +202,18 @@ public class Session implements SessionHandler.SessionIf * @param handler the SessionHandler managing the session * @param data the session data */ - public Session (SessionHandler handler, SessionData data) + public Session(SessionHandler handler, SessionData data) { _handler = handler; _sessionData = data; _sessionInactivityTimer = new SessionInactivityTimer(); } - + /* ------------------------------------------------------------- */ /** - * Returns the current number of requests that are active in the - * Session. - * + * Returns the current number of requests that are active in the Session. + * * @return the number of active requests for this session */ public long getRequests() @@ -213,23 +223,21 @@ public class Session implements SessionHandler.SessionIf return _requests; } } - - - + /* ------------------------------------------------------------- */ - public void setExtendedId (String extendedId) + public void setExtendedId(String extendedId) { _extendedId = extendedId; } - + /* ------------------------------------------------------------- */ protected void cookieSet() { try (Lock lock = _lock.lock()) { - _sessionData.setCookieSet(_sessionData.getAccessed()); + _sessionData.setCookieSet(_sessionData.getAccessed()); } } /* ------------------------------------------------------------ */ @@ -239,7 +247,7 @@ public class Session implements SessionHandler.SessionIf { if (!isValid()) return false; - _newSession=false; + _newSession = false; long lastAccessed = _sessionData.getAccessed(); _sessionData.setAccessed(time); _sessionData.setLastAccessed(lastAccessed); @@ -251,8 +259,9 @@ public class Session implements SessionHandler.SessionIf } _requests++; - //temporarily stop the idle timer - if (LOG.isDebugEnabled()) LOG.debug("Session {} accessed, stopping timer, active requests={}",getId(),_requests); + // temporarily stop the idle timer + if (LOG.isDebugEnabled()) + LOG.debug("Session {} accessed, stopping timer, active requests={}", getId(), _requests); _sessionInactivityTimer.cancel(); @@ -267,18 +276,19 @@ public class Session implements SessionHandler.SessionIf { _requests--; - if (LOG.isDebugEnabled()) LOG.debug("Session {} complete, active requests={}",getId(),_requests); + if (LOG.isDebugEnabled()) + LOG.debug("Session {} complete, active requests={}", getId(), _requests); - //start the inactivity timer + // start the inactivity timer if (_requests == 0) _sessionInactivityTimer.schedule(); } } - /* ------------------------------------------------------------- */ - /** Check to see if session has expired as at the time given. + /** + * Check to see if session has expired as at the time given. * * @param time the time since the epoch in ms * @return true if expired @@ -290,83 +300,85 @@ public class Session implements SessionHandler.SessionIf return _sessionData.isExpiredAt(time); } } - - + /* ------------------------------------------------------------- */ - /** Check if the Session has been idle longer than a number of seconds. + /** + * Check if the Session has been idle longer than a number of seconds. * * @param sec the number of seconds * @return true if the session has been idle longer than the interval */ - protected boolean isIdleLongerThan (int sec) + protected boolean isIdleLongerThan(int sec) { long now = System.currentTimeMillis(); try (Lock lock = _lock.lock()) { - return ((_sessionData.getAccessed() + (sec*1000)) <= now); + return ((_sessionData.getAccessed() + (sec * 1000)) <= now); } } - - + /* ------------------------------------------------------------ */ /** - * Call binding and attribute listeners based on the new and old - * values of the attribute. + * Call binding and attribute listeners based on the new and old values of + * the attribute. * * @param name name of the attribute - * @param newValue new value of the attribute + * @param newValue new value of the attribute * @param oldValue previous value of the attribute * @throws IllegalStateException if no session manager can be find */ - protected void callSessionAttributeListeners (String name, Object newValue, Object oldValue) + protected void callSessionAttributeListeners(String name, Object newValue, Object oldValue) { - if (newValue==null || !newValue.equals(oldValue)) + if (newValue == null || !newValue.equals(oldValue)) { - if (oldValue!=null) - unbindValue(name,oldValue); - if (newValue!=null) - bindValue(name,newValue); + if (oldValue != null) + unbindValue(name, oldValue); + if (newValue != null) + bindValue(name, newValue); if (_handler == null) - throw new IllegalStateException ("No session manager for session "+ _sessionData.getId()); - - _handler.doSessionAttributeListeners(this,name,oldValue,newValue); + throw new IllegalStateException("No session manager for session " + _sessionData.getId()); + + _handler.doSessionAttributeListeners(this, name, oldValue, newValue); } } - - + /* ------------------------------------------------------------- */ /** - * Unbind value if value implements {@link HttpSessionBindingListener} (calls {@link HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent)}) - * @param name the name with which the object is bound or unbound + * Unbind value if value implements {@link HttpSessionBindingListener} + * (calls + * {@link HttpSessionBindingListener#valueUnbound(HttpSessionBindingEvent)}) + * + * @param name the name with which the object is bound or unbound * @param value the bound value */ public void unbindValue(java.lang.String name, Object value) { - if (value!=null&&value instanceof HttpSessionBindingListener) - ((HttpSessionBindingListener)value).valueUnbound(new HttpSessionBindingEvent(this,name)); - } - - - /* ------------------------------------------------------------- */ - /** - * Bind value if value implements {@link HttpSessionBindingListener} (calls {@link HttpSessionBindingListener#valueBound(HttpSessionBindingEvent)}) - * @param name the name with which the object is bound or unbound - * @param value the bound value - */ - public void bindValue(java.lang.String name, Object value) - { - if (value!=null&&value instanceof HttpSessionBindingListener) - ((HttpSessionBindingListener)value).valueBound(new HttpSessionBindingEvent(this,name)); + if (value != null && value instanceof HttpSessionBindingListener) + ((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name)); } /* ------------------------------------------------------------- */ /** - * Call the activation listeners. This must be called holding the - * lock. + * Bind value if value implements {@link HttpSessionBindingListener} (calls + * {@link HttpSessionBindingListener#valueBound(HttpSessionBindingEvent)}) + * + * @param name the name with which the object is bound or unbound + * @param value the bound value + */ + public void bindValue(java.lang.String name, Object value) + { + if (value != null && value instanceof HttpSessionBindingListener) + ((HttpSessionBindingListener) value).valueBound(new HttpSessionBindingEvent(this, name)); + } + + + /* ------------------------------------------------------------- */ + /** + * Call the activation listeners. This must be called holding the lock. */ public void didActivate() { @@ -385,8 +397,7 @@ public class Session implements SessionHandler.SessionIf /* ------------------------------------------------------------- */ /** - * Call the passivation listeners. This must be called holding the - * lock + * Call the passivation listeners. This must be called holding the lock */ public void willPassivate() { @@ -407,10 +418,16 @@ public class Session implements SessionHandler.SessionIf { try (Lock lock = _lock.lock()) { - return _state==State.VALID; + return _state == State.VALID; } } + /* ------------------------------------------------------------ */ + public boolean isChanging() + { + checkLocked(); + return _state == State.CHANGING; + } /* ------------------------------------------------------------- */ public long getCookieSetTime() @@ -433,9 +450,7 @@ public class Session implements SessionHandler.SessionIf } } - - - /** + /** * @see javax.servlet.http.HttpSession#getId() */ @Override @@ -447,25 +462,24 @@ public class Session implements SessionHandler.SessionIf } } - + public String getExtendedId() { return _extendedId; } - + public String getContextPath() { return _sessionData.getContextPath(); } - - public String getVHost () + public String getVHost() { return _sessionData.getVhost(); } - - /** + + /** * @see javax.servlet.http.HttpSession#getLastAccessedTime() */ @Override @@ -477,18 +491,18 @@ public class Session implements SessionHandler.SessionIf } } - /** + /** * @see javax.servlet.http.HttpSession#getServletContext() */ @Override public ServletContext getServletContext() { if (_handler == null) - throw new IllegalStateException ("No session manager for session "+ _sessionData.getId()); - return _handler._context; + throw new IllegalStateException("No session manager for session " + _sessionData.getId()); + return _handler._context; } - /** + /** * @see javax.servlet.http.HttpSession#setMaxInactiveInterval(int) */ @Override @@ -496,7 +510,7 @@ public class Session implements SessionHandler.SessionIf { try (Lock lock = _lock.lock()) { - _sessionData.setMaxInactiveMs((long)secs*1000L); + _sessionData.setMaxInactiveMs((long) secs * 1000L); _sessionData.calcAndSetExpiry(); _sessionData.setDirty(true); updateInactivityTimer(); @@ -509,63 +523,68 @@ public class Session implements SessionHandler.SessionIf } } } - - + /** - * Set the inactivity timer to the smaller of the session maxInactivity - * (ie session-timeout from web.xml), or the inactive eviction time. + * Set the inactivity timer to the smaller of the session maxInactivity (ie + * session-timeout from web.xml), or the inactive eviction time. */ - public void updateInactivityTimer () + public void updateInactivityTimer() { try (Lock lock = _lock.lock()) { - long maxInactive = _sessionData.getMaxInactiveMs(); + long maxInactive = _sessionData.getMaxInactiveMs(); int evictionPolicy = getSessionHandler().getSessionCache().getEvictionPolicy(); if (maxInactive <= 0) { - //sessions are immortal, they never expire + // sessions are immortal, they never expire if (evictionPolicy < SessionCache.EVICT_ON_INACTIVITY) { - //we do not want to evict inactive sessions + // we do not want to evict inactive sessions _sessionInactivityTimer.setTimeout(-1); - if (LOG.isDebugEnabled()) LOG.debug("Session {} is immortal && no inactivity eviction", getId()); + if (LOG.isDebugEnabled()) + LOG.debug("Session {} is immortal && no inactivity eviction", getId()); } else { - //sessions are immortal but we want to evict after inactivity + // sessions are immortal but we want to evict after + // inactivity _sessionInactivityTimer.setTimeout(TimeUnit.SECONDS.toMillis(evictionPolicy)); - if (LOG.isDebugEnabled()) LOG.debug("Session {} is immortal; evict after {} sec inactivity", getId(), evictionPolicy); - } + if (LOG.isDebugEnabled()) + LOG.debug("Session {} is immortal; evict after {} sec inactivity", getId(), evictionPolicy); + } } else { - //sessions are not immortal + // sessions are not immortal if (evictionPolicy == SessionCache.NEVER_EVICT) { - //timeout is just the maxInactive setting + // timeout is just the maxInactive setting _sessionInactivityTimer.setTimeout(_sessionData.getMaxInactiveMs()); - if (LOG.isDebugEnabled()) LOG.debug("Session {} no eviction", getId()); + if (LOG.isDebugEnabled()) + LOG.debug("Session {} no eviction", getId()); } else if (evictionPolicy == SessionCache.EVICT_ON_SESSION_EXIT) { - //session will not remain in the cache, so no timeout + // session will not remain in the cache, so no timeout _sessionInactivityTimer.setTimeout(-1); - if (LOG.isDebugEnabled()) LOG.debug("Session {} evict on exit", getId()); + if (LOG.isDebugEnabled()) + LOG.debug("Session {} evict on exit", getId()); } else { - //want to evict on idle: timer is lesser of the session's maxInactive and eviction timeout + // want to evict on idle: timer is lesser of the session's + // maxInactive and eviction timeout _sessionInactivityTimer.setTimeout(Math.min(maxInactive, TimeUnit.SECONDS.toMillis(evictionPolicy))); - if (LOG.isDebugEnabled()) LOG.debug("Session {} timer set to lesser of maxInactive={} and inactivityEvict={}", getId(), maxInactive, evictionPolicy); + if (LOG.isDebugEnabled()) + LOG.debug("Session {} timer set to lesser of maxInactive={} and inactivityEvict={}", getId(), maxInactive, evictionPolicy); } } } } - - /** + /** * @see javax.servlet.http.HttpSession#getMaxInactiveInterval() */ @Override @@ -574,11 +593,11 @@ public class Session implements SessionHandler.SessionIf try (Lock lock = _lock.lock()) { long maxInactiveMs = _sessionData.getMaxInactiveMs(); - return (int)(maxInactiveMs < 0 ? -1 : maxInactiveMs/1000); + return (int) (maxInactiveMs < 0 ? -1 : maxInactiveMs / 1000); } } - /** + /** * @see javax.servlet.http.HttpSession#getSessionContext() */ @Override @@ -589,13 +608,12 @@ public class Session implements SessionHandler.SessionIf return SessionHandler.__nullSessionContext; } - + public SessionHandler getSessionHandler() { return _handler; } - - + /* ------------------------------------------------------------- */ /** * Check that the session can be modified. @@ -603,50 +621,68 @@ public class Session implements SessionHandler.SessionIf * @throws IllegalStateException if the session is invalid */ protected void checkValidForWrite() throws IllegalStateException - { + { checkLocked(); if (_state == State.INVALID) - throw new IllegalStateException("Not valid for write: id="+_sessionData.getId()+" created="+_sessionData.getCreated()+" accessed="+_sessionData.getAccessed()+" lastaccessed="+_sessionData.getLastAccessed()+" maxInactiveMs="+_sessionData.getMaxInactiveMs()+" expiry="+_sessionData.getExpiry()); - + throw new IllegalStateException("Not valid for write: id=" + _sessionData.getId() + + " created=" + + _sessionData.getCreated() + + " accessed=" + + _sessionData.getAccessed() + + " lastaccessed=" + + _sessionData.getLastAccessed() + + " maxInactiveMs=" + + _sessionData.getMaxInactiveMs() + + " expiry=" + + _sessionData.getExpiry()); + if (_state == State.INVALIDATING) - return; //in the process of being invalidated, listeners may try to remove attributes - + return; // in the process of being invalidated, listeners may try to + // remove attributes + if (!isResident()) - throw new IllegalStateException("Not valid for write: id="+_sessionData.getId()+" not resident"); + throw new IllegalStateException("Not valid for write: id=" + _sessionData.getId() + " not resident"); } - - + /* ------------------------------------------------------------- */ /** * Chech that the session data can be read. * * @throws IllegalStateException if the session is invalid */ - protected void checkValidForRead () throws IllegalStateException + protected void checkValidForRead() throws IllegalStateException { checkLocked(); - + if (_state == State.INVALID) - throw new IllegalStateException("Invalid for read: id="+_sessionData.getId()+" created="+_sessionData.getCreated()+" accessed="+_sessionData.getAccessed()+" lastaccessed="+_sessionData.getLastAccessed()+" maxInactiveMs="+_sessionData.getMaxInactiveMs()+" expiry="+_sessionData.getExpiry()); - + throw new IllegalStateException("Invalid for read: id=" + _sessionData.getId() + + " created=" + + _sessionData.getCreated() + + " accessed=" + + _sessionData.getAccessed() + + " lastaccessed=" + + _sessionData.getLastAccessed() + + " maxInactiveMs=" + + _sessionData.getMaxInactiveMs() + + " expiry=" + + _sessionData.getExpiry()); + if (_state == State.INVALIDATING) return; - + if (!isResident()) - throw new IllegalStateException("Invalid for read: id="+_sessionData.getId()+" not resident"); + throw new IllegalStateException("Invalid for read: id=" + _sessionData.getId() + " not resident"); } - - + /* ------------------------------------------------------------- */ - protected void checkLocked () - throws IllegalStateException + protected void checkLocked() throws IllegalStateException { if (!_lock.isLocked()) throw new IllegalStateException("Session not locked"); } - /** + /** * @see javax.servlet.http.HttpSession#getAttribute(java.lang.String) */ @Override @@ -659,7 +695,7 @@ public class Session implements SessionHandler.SessionIf } } - /** + /** * @see javax.servlet.http.HttpSession#getValue(java.lang.String) */ @Override @@ -667,12 +703,12 @@ public class Session implements SessionHandler.SessionIf public Object getValue(String name) { try (Lock lock = _lock.lock()) - { + { return _sessionData.getAttribute(name); } } - /** + /** * @see javax.servlet.http.HttpSession#getAttributeNames() */ @Override @@ -682,7 +718,7 @@ public class Session implements SessionHandler.SessionIf { checkValidForRead(); final Iterator itor = _sessionData.getKeys().iterator(); - return new Enumeration () + return new Enumeration() { @Override @@ -702,8 +738,6 @@ public class Session implements SessionHandler.SessionIf } - - /* ------------------------------------------------------------ */ public int getAttributes() { @@ -711,8 +745,6 @@ public class Session implements SessionHandler.SessionIf } - - /* ------------------------------------------------------------ */ public Set getNames() { @@ -743,97 +775,145 @@ public class Session implements SessionHandler.SessionIf } /* ------------------------------------------------------------- */ - /** - * @see javax.servlet.http.HttpSession#setAttribute(java.lang.String, java.lang.Object) + /** + * @see javax.servlet.http.HttpSession#setAttribute(java.lang.String, + * java.lang.Object) */ @Override public void setAttribute(String name, Object value) { - Object old=null; + Object old = null; try (Lock lock = _lock.lock()) { - //if session is not valid, don't accept the set + // if session is not valid, don't accept the set checkValidForWrite(); - old=_sessionData.setAttribute(name,value); + old = _sessionData.setAttribute(name, value); } if (value == null && old == null) - return; //if same as remove attribute but attribute was already removed, no change + return; // if same as remove attribute but attribute was already + // removed, no change callSessionAttributeListeners(name, value, old); } - - - + /* ------------------------------------------------------------- */ - /** - * @see javax.servlet.http.HttpSession#putValue(java.lang.String, java.lang.Object) + /** + * @see javax.servlet.http.HttpSession#putValue(java.lang.String, + * java.lang.Object) */ @Override @Deprecated public void putValue(String name, Object value) { - setAttribute(name,value); + setAttribute(name, value); } - - - + /* ------------------------------------------------------------- */ - /** + /** * @see javax.servlet.http.HttpSession#removeAttribute(java.lang.String) */ @Override public void removeAttribute(String name) { - setAttribute(name, null); + setAttribute(name, null); } - - - + /* ------------------------------------------------------------- */ - /** + /** * @see javax.servlet.http.HttpSession#removeValue(java.lang.String) */ @Override @Deprecated public void removeValue(String name) { - setAttribute(name, null); + setAttribute(name, null); } /* ------------------------------------------------------------ */ - /** Force a change to the id of a session. + /** + * Force a change to the id of a session. * * @param request the Request associated with the call to change id. */ public void renewId(HttpServletRequest request) { if (_handler == null) - throw new IllegalStateException ("No session manager for session "+ _sessionData.getId()); - + throw new IllegalStateException("No session manager for session " + _sessionData.getId()); + String id = null; String extendedId = null; try (Lock lock = _lock.lock()) { - checkValidForWrite(); //don't renew id on a session that is not valid - id = _sessionData.getId(); //grab the values as they are now + while (true) + { + switch (_state) + { + case INVALID: + case INVALIDATING: + throw new IllegalStateException(); + + case CHANGING: + try + { + _stateChangeCompleted.await(); + } + catch (InterruptedException e) + { + throw new RuntimeException(e); + } + continue; + + case VALID: + _state = State.CHANGING; + break; + default: + throw new IllegalStateException(); + } + break; + } + + id = _sessionData.getId(); // grab the values as they are now extendedId = getExtendedId(); } - - String newId = _handler._sessionIdManager.renewSessionId(id, extendedId, request); + + + String newId = _handler._sessionIdManager.renewSessionId(id, extendedId, request); + try (Lock lock = _lock.lock()) { - checkValidForWrite(); - _sessionData.setId(newId); - setExtendedId(_handler._sessionIdManager.getExtendedId(newId, request)); + switch (_state) + { + case CHANGING: + if (id.equals(newId)) + throw new IllegalStateException("Unable to change session id"); + + // this shouldn't be necessary to do here EXCEPT that when a + // null session cache is + // used, a new Session object will be created during the + // call to renew, so this + // Session object will not have been modified. + _sessionData.setId(newId); + setExtendedId(_handler._sessionIdManager.getExtendedId(newId, request)); + setIdChanged(true); + + _state = State.VALID; + _stateChangeCompleted.signalAll(); + break; + + case INVALID: + case INVALIDATING: + throw new IllegalStateException("Session invalid"); + + default: + throw new IllegalStateException(); + } } - setIdChanged(true); } - - + /* ------------------------------------------------------------- */ - /** Called by users to invalidate a session, or called by the - * access method as a request enters the session if the session - * has expired, or called by manager as a result of scavenger - * expiring session + /** + * Called by users to invalidate a session, or called by the access method + * as a request enters the session if the session has expired, or called by + * manager as a result of scavenger expiring session * * @see javax.servlet.http.HttpSession#invalidate() */ @@ -841,16 +921,28 @@ public class Session implements SessionHandler.SessionIf public void invalidate() { if (_handler == null) - throw new IllegalStateException ("No session manager for session "+ _sessionData.getId()); + throw new IllegalStateException("No session manager for session " + _sessionData.getId()); boolean result = beginInvalidate(); try { - //if the session was not already invalid, or in process of being invalidated, do invalidate + // if the session was not already invalid, or in process of being + // invalidated, do invalidate if (result) { - //tell id mgr to remove session from all contexts + try + { + // do the invalidation + _handler.callSessionDestroyedListeners(this); + } + finally + { + // call the attribute removed listeners and finally mark it + // as invalid + finishInvalidate(); + } + // tell id mgr to remove sessions with same id from all contexts _handler.getSessionIdManager().invalidateAll(_sessionData.getId()); } } @@ -861,24 +953,27 @@ public class Session implements SessionHandler.SessionIf } /* ------------------------------------------------------------- */ - /** Grab the lock on the session + /** + * Grab the lock on the session + * * @return the lock */ - public Lock lock () + public Lock lock() { return _lock.lock(); } - - + /* ------------------------------------------------------------- */ - /** Grab the lock on the session if it isn't locked already + /** + * Grab the lock on the session if it isn't locked already + * * @return the lock */ - public Lock lockIfNotHeld () + public Lock lockIfNotHeld() { return _lock.lock(); } - + /* ------------------------------------------------------------- */ /** * @return true if the session is not already invalid or being invalidated. @@ -886,29 +981,54 @@ public class Session implements SessionHandler.SessionIf protected boolean beginInvalidate() { boolean result = false; - + try (Lock lock = _lock.lock()) { - switch (_state) + + while (true) { - case INVALID: + switch (_state) { - throw new IllegalStateException(); //spec does not allow invalidate of already invalid session - } - case VALID: - { - //only first change from valid to invalidating should be actionable - result = true; - _state = State.INVALIDATING; - break; - } - default: - { - if (LOG.isDebugEnabled()) LOG.debug("Session {} already being invalidated", _sessionData.getId()); + case INVALID: + { + throw new IllegalStateException(); // spec does not + // allow invalidate + // of already invalid + // session + } + case INVALIDATING: + { + if (LOG.isDebugEnabled()) + LOG.debug("Session {} already being invalidated", _sessionData.getId()); + break; + } + case CHANGING: + { + try + { + _stateChangeCompleted.await(); + } + catch (InterruptedException e) + { + throw new RuntimeException(e); + } + continue; + } + case VALID: + { + // only first change from valid to invalidating should + // be actionable + result = true; + _state = State.INVALIDATING; + break; + } + default: + throw new IllegalStateException(); } + break; } } - + return result; } @@ -918,16 +1038,15 @@ public class Session implements SessionHandler.SessionIf * * @throws IllegalStateException if no session manager can be find */ - @Deprecated + @Deprecated protected void doInvalidate() throws IllegalStateException { - finishInvalidate(); + finishInvalidate(); } - - + /* ------------------------------------------------------------- */ - /** Call HttpSessionAttributeListeners as part of invalidating - * a Session. + /** + * Call HttpSessionAttributeListeners as part of invalidating a Session. * * @throws IllegalStateException if no session manager can be find */ @@ -938,21 +1057,22 @@ public class Session implements SessionHandler.SessionIf try { if (LOG.isDebugEnabled()) - LOG.debug("invalidate {}",_sessionData.getId()); + LOG.debug("invalidate {}", _sessionData.getId()); if (_state == State.VALID || _state == State.INVALIDATING) { Set keys = null; do { keys = _sessionData.getKeys(); - for (String key:keys) + for (String key : keys) { - Object old=_sessionData.setAttribute(key,null); + Object old = _sessionData.setAttribute(key, null); + // if same as remove attribute but attribute was + // already removed, no change if (old == null) - return; //if same as remove attribute but attribute was already removed, no change + continue; callSessionAttributeListeners(key, null, old); } - } while (!keys.isEmpty()); } @@ -961,6 +1081,8 @@ public class Session implements SessionHandler.SessionIf { // mark as invalid _state = State.INVALID; + _handler.recordSessionTime(this); + _stateChangeCompleted.signalAll(); } } } @@ -975,29 +1097,26 @@ public class Session implements SessionHandler.SessionIf return _newSession; } } - - + /* ------------------------------------------------------------- */ public void setIdChanged(boolean changed) { try (Lock lock = _lock.lock()) { - _idChanged=changed; + _idChanged = changed; } } - - + /* ------------------------------------------------------------- */ - public boolean isIdChanged () + public boolean isIdChanged() { try (Lock lock = _lock.lock()) { return _idChanged; } } - - + /* ------------------------------------------------------------- */ @Override public Session getSession() @@ -1005,7 +1124,7 @@ public class Session implements SessionHandler.SessionIf // TODO why is this used return this; } - + /* ------------------------------------------------------------- */ protected SessionData getSessionData() { @@ -1013,7 +1132,7 @@ public class Session implements SessionHandler.SessionIf } /* ------------------------------------------------------------- */ - public void setResident (boolean resident) + public void setResident(boolean resident) { _resident = resident; @@ -1024,9 +1143,9 @@ public class Session implements SessionHandler.SessionIf } /* ------------------------------------------------------------- */ - public boolean isResident () + public boolean isResident() { return _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 52344a3bce5..ecf99fd1ae3 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 @@ -91,16 +91,41 @@ public interface SessionCache extends LifeCycle */ Session newSession (SessionData data); + /** + * Change the id of a session. + * + * This method has been superceded by the 4 arg renewSessionId method and + * should no longer be called. + * + * @param oldId the old id + * @param newId the new id + * @return the changed Session + * @throws Exception if anything went wrong + * @deprecated use + * {@link #renewSessionId(String oldId, String newId, String oldExtendedId, String newExtendedId)} + */ + @Deprecated + default Session renewSessionId(String oldId, String newId) throws Exception + { + return null; + } + /** * Change the id of a Session. * * @param oldId the current session id * @param newId the new session id + * @param oldExtendedId the current extended session id + * @param newExtendedId the new extended session id * @return the Session after changing its id * @throws Exception if any error occurred */ - Session renewSessionId (String oldId, String newId) throws Exception; + default Session renewSessionId(String oldId, String newId, String oldExtendedId, String newExtendedId) throws Exception + { + return renewSessionId(oldId, newId); + } + /** 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 5dde83b28b6..abcd95afb3a 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 @@ -18,6 +18,8 @@ package org.eclipse.jetty.server.session; +import static java.lang.Math.round; + import java.io.IOException; import java.util.Arrays; import java.util.Collections; @@ -65,8 +67,6 @@ import org.eclipse.jetty.util.thread.Locker.Lock; import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; import org.eclipse.jetty.util.thread.Scheduler; -import static java.lang.Math.round; - /* ------------------------------------------------------------ */ /** * SessionHandler. @@ -343,6 +343,60 @@ public class SessionHandler extends ScopedHandler _sessionListeners.clear(); _sessionIdListeners.clear(); } + + + /** + * Call the session lifecycle listeners + * @param session the session on which to call the lifecycle listeners + */ + protected void callSessionDestroyedListeners (Session session) + { + if (session == null) + return; + + if (_sessionListeners!=null) + { + HttpSessionEvent event=new HttpSessionEvent(session); + for (int i = _sessionListeners.size()-1; i>=0; i--) + { + _sessionListeners.get(i).sessionDestroyed(event); + } + } + } + + /** + * Call the session lifecycle listeners + * @param session the session on which to call the lifecycle listeners + */ + protected void callSessionCreatedListeners (Session session) + { + if (session == null) + return; + + if (_sessionListeners!=null) + { + HttpSessionEvent event=new HttpSessionEvent(session); + for (int i = _sessionListeners.size()-1; i>=0; i--) + { + _sessionListeners.get(i).sessionCreated(event); + } + } + } + + + protected void callSessionIdListeners (Session session, String oldId) + { + //inform the listeners + if (!_sessionIdListeners.isEmpty()) + { + HttpSessionEvent event = new HttpSessionEvent(session); + for (HttpSessionIdListener l:_sessionIdListeners) + { + l.sessionIdChanged(event, oldId); + } + } + } + /* ------------------------------------------------------------ */ /** @@ -793,15 +847,10 @@ public class SessionHandler extends ScopedHandler _sessionCache.put(id, session); _sessionsCreatedStats.increment(); - if (request.isSecure()) + if (request!=null && request.isSecure()) session.setAttribute(Session.SESSION_CREATED_SECURE, Boolean.TRUE); - if (_sessionListeners!=null) - { - HttpSessionEvent event=new HttpSessionEvent(session); - for (HttpSessionListener listener : _sessionListeners) - listener.sessionCreated(event); - } + callSessionCreatedListeners(session); return session; } @@ -1183,24 +1232,15 @@ public class SessionHandler extends ScopedHandler { try { - Session session = _sessionCache.renewSessionId (oldId, newId); //swap the id over + Session session = _sessionCache.renewSessionId (oldId, newId, oldExtendedId, newExtendedId); //swap the id over if (session == null) { //session doesn't exist on this context return; } - - session.setExtendedId(newExtendedId); //remember the extended id //inform the listeners - if (!_sessionIdListeners.isEmpty()) - { - HttpSessionEvent event = new HttpSessionEvent(session); - for (HttpSessionIdListener l:_sessionIdListeners) - { - l.sessionIdChanged(event, oldId); - } - } + callSessionIdListeners(session, oldId); } catch (Exception e) { @@ -1208,28 +1248,62 @@ public class SessionHandler extends ScopedHandler } } - + /** + * Record length of time session has been active. Called when the + * session is about to be invalidated. + * + * @param session the session whose time to record + */ + protected void recordSessionTime (Session session) + { + _sessionTimeStats.record(round((System.currentTimeMillis() - session.getSessionData().getCreated())/1000.0)); + } + /* ------------------------------------------------------------ */ /** - * Called when a session has expired. + * Called by SessionIdManager to remove a session that has been invalidated, + * either by this context or another context. Also called by + * SessionIdManager when a session has expired in either this context or + * another context. * - * @param id the id to invalidate + * @param id the session id to invalidate */ public void invalidate (String id) { + if (StringUtil.isBlank(id)) return; try - { - //remove the session and call the destroy listeners - Session session = removeSession(id, true); - + { + // Remove the Session object from the session cache and any backing + // data store + Session session = _sessionCache.delete(id); if (session != null) { - _sessionTimeStats.record(round((System.currentTimeMillis() - session.getSessionData().getCreated())/1000.0)); - session.finishInvalidate(); + //start invalidating if it is not already begun, and call the listeners + try + { + if (session.beginInvalidate()) + { + try + { + callSessionDestroyedListeners(session); + } + catch (Exception e) + { + LOG.warn("Session listener threw exception", e); + } + //call the attribute removed listeners and finally mark it as invalid + session.finishInvalidate(); + } + } + catch (IllegalStateException e) + { + if (LOG.isDebugEnabled()) LOG.debug("Session {} already invalid", session); + LOG.ignore(e); + } } } catch (Exception e) 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 a3436e56a98..9cb25844567 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 @@ -34,7 +34,9 @@ import java.util.concurrent.TimeUnit; 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.server.SessionIdManager; import org.eclipse.jetty.servlet.ServletContextHandler; import org.junit.jupiter.api.Test; @@ -65,6 +67,100 @@ public class DefaultSessionCacheTest } + @Test + public void testRenewIdMultipleRequests() throws Exception + { + //Test that invalidation happens on ALL copies of the session that are in-use by requests + Server server = new Server(); + + SessionIdManager sessionIdManager = new DefaultSessionIdManager(server); + server.setSessionIdManager(sessionIdManager); + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/test"); + context.setServer(server); + context.getSessionHandler().setMaxInactiveInterval((int)TimeUnit.DAYS.toSeconds(1)); + context.getSessionHandler().setSessionIdManager(sessionIdManager); + + DefaultSessionCacheFactory cacheFactory = new DefaultSessionCacheFactory(); + cacheFactory.setSaveOnCreate(true); //ensures that a session is persisted as soon as it is created + + DefaultSessionCache cache = (DefaultSessionCache)cacheFactory.getSessionCache(context.getSessionHandler()); + + TestSessionDataStore store = new TestSessionDataStore(); + cache.setSessionDataStore(store); + context.getSessionHandler().setSessionCache(cache); + TestHttpSessionListener listener = new TestHttpSessionListener(); + context.getSessionHandler().addEventListener(listener); + + server.setHandler(context); + try + { + server.start(); + + //create a new session + Session s = (Session)context.getSessionHandler().newHttpSession(null); + String id = s.getId(); + context.getSessionHandler().access(s, false); //simulate accessing the request + context.getSessionHandler().complete(s); //simulate completing the request + + //make 1st request + final Session session = context.getSessionHandler().getSession(id); //get the session again + assertNotNull(session); + context.getSessionHandler().access(session, false); //simulate accessing the request + + + + //make 2nd request + final Session session2 = context.getSessionHandler().getSession(id); //get the session again + context.getSessionHandler().access(session2, false); //simulate accessing the request + assertNotNull(session2); + assertTrue(session == session2); + + + + Thread t2 = new Thread(new Runnable() + { + @Override + public void run() + { + System.err.println("Starting session id renewal"); + session2.renewId(new Request(null,null)); + System.err.println("Finished session id renewal"); + } + }); + t2.start(); + + + + Thread t = new Thread(new Runnable() + { + + @Override + public void run() + { + System.err.println("Starting invalidation"); + try{Thread.sleep(1000L);}catch (Exception e) {e.printStackTrace();} + session.invalidate(); + System.err.println("Finished invalidation"); + } + } + ); + t.start(); + + t.join(); + t2.join(); + + } + finally + { + server.stop(); + } + } + + + + + /** * Test sessions are saved when shutdown with a store. @@ -180,7 +276,7 @@ public class DefaultSessionCacheTest cache.put("1234", session); assertTrue(cache.contains("1234")); - cache.renewSessionId("1234", "5678"); + cache.renewSessionId("1234", "5678", "1234.foo", "5678.foo"); assertTrue(cache.contains("5678")); assertFalse(cache.contains("1234")); From 92317a7bb41b25121bf09ef2d90947c79d8e71d6 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Fri, 23 Nov 2018 16:54:36 +0100 Subject: [PATCH 06/10] Issue #3132 improve dump readability fixed tests Signed-off-by: Greg Wilkins --- ...umptTest.java => ClassLoaderDumpTest.java} | 48 +++---- .../component/ContainerLifeCycleTest.java | 118 +++++++++--------- 2 files changed, 83 insertions(+), 83 deletions(-) rename jetty-server/src/test/java/org/eclipse/jetty/server/{ClassLoaderDumptTest.java => ClassLoaderDumpTest.java} (74%) diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ClassLoaderDumptTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ClassLoaderDumpTest.java similarity index 74% rename from jetty-server/src/test/java/org/eclipse/jetty/server/ClassLoaderDumptTest.java rename to jetty-server/src/test/java/org/eclipse/jetty/server/ClassLoaderDumpTest.java index 3354d9511a6..3f8778511d6 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ClassLoaderDumptTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ClassLoaderDumpTest.java @@ -29,7 +29,7 @@ import java.net.URLClassLoader; import org.eclipse.jetty.util.component.Dumpable; import org.junit.jupiter.api.Test; -public class ClassLoaderDumptTest +public class ClassLoaderDumpTest { @Test public void testSimple() throws Exception @@ -48,8 +48,8 @@ public class ClassLoaderDumptTest StringBuilder out = new StringBuilder(); server.dump(out); String dump = out.toString(); - assertThat(dump,containsString("+-SimpleLoader")); - assertThat(dump,containsString("+>"+Server.class.getClassLoader())); + assertThat(dump,containsString("+- SimpleLoader")); + assertThat(dump,containsString("+> "+Server.class.getClassLoader())); } @Test @@ -69,9 +69,9 @@ public class ClassLoaderDumptTest StringBuilder out = new StringBuilder(); server.dump(out); String dump = out.toString(); - assertThat(dump,containsString("+-ParentedLoader")); - assertThat(dump,containsString("| +>"+Server.class.getClassLoader())); - assertThat(dump,containsString("+>"+Server.class.getClassLoader())); + assertThat(dump,containsString("+- ParentedLoader")); + assertThat(dump,containsString("| +> "+Server.class.getClassLoader())); + assertThat(dump,containsString("+> "+Server.class.getClassLoader())); } @Test @@ -98,10 +98,10 @@ public class ClassLoaderDumptTest StringBuilder out = new StringBuilder(); server.dump(out); String dump = out.toString(); - assertThat(dump,containsString("+-TopLoader")); - assertThat(dump,containsString("| +>MiddleLoader")); - assertThat(dump,containsString("| +>"+Server.class.getClassLoader())); - assertThat(dump,containsString("+>"+Server.class.getClassLoader())); + assertThat(dump,containsString("+- TopLoader")); + assertThat(dump,containsString("| +> MiddleLoader")); + assertThat(dump,containsString("| +> "+Server.class.getClassLoader())); + assertThat(dump,containsString("+> "+Server.class.getClassLoader())); } @Test @@ -122,10 +122,10 @@ public class ClassLoaderDumptTest StringBuilder out = new StringBuilder(); server.dump(out); String dump = out.toString(); - assertThat(dump,containsString("+-TopLoader")); - assertThat(dump,containsString("| +>DumpableClassLoader")); - assertThat(dump,not(containsString("| +>"+Server.class.getClassLoader()))); - assertThat(dump,containsString("+>"+Server.class.getClassLoader())); + assertThat(dump,containsString("+- TopLoader")); + assertThat(dump,containsString("| +> DumpableClassLoader")); + assertThat(dump,not(containsString("| +> "+Server.class.getClassLoader()))); + assertThat(dump,containsString("+> "+Server.class.getClassLoader())); } public static class DumpableClassLoader extends ClassLoader implements Dumpable @@ -184,15 +184,15 @@ public class ClassLoaderDumptTest server.dump(out); String dump = out.toString(); // System.err.println(dump); - assertThat(dump,containsString("+-TopLoader")); - assertThat(dump,containsString("| | +>file:/ONE")); - assertThat(dump,containsString("| | +>file:/TWO")); - assertThat(dump,containsString("| | +>file:/THREE")); - assertThat(dump,containsString("| +>MiddleLoader")); - assertThat(dump,containsString("| | +>file:/one")); - assertThat(dump,containsString("| | +>file:/two")); - assertThat(dump,containsString("| | +>file:/three")); - assertThat(dump,containsString("| +>"+Server.class.getClassLoader())); - assertThat(dump,containsString("+>"+Server.class.getClassLoader())); + assertThat(dump,containsString("+- TopLoader")); + assertThat(dump,containsString("| | +> file:/ONE")); + assertThat(dump,containsString("| | +> file:/TWO")); + assertThat(dump,containsString("| | +> file:/THREE")); + assertThat(dump,containsString("| +> MiddleLoader")); + assertThat(dump,containsString("| | +> file:/one")); + assertThat(dump,containsString("| | +> file:/two")); + assertThat(dump,containsString("| | +> file:/three")); + assertThat(dump,containsString("| +> "+Server.class.getClassLoader())); + assertThat(dump,containsString("+> "+Server.class.getClassLoader())); } } diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/component/ContainerLifeCycleTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/component/ContainerLifeCycleTest.java index 1ad73ec321f..019af01bd25 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/component/ContainerLifeCycleTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/component/ContainerLifeCycleTest.java @@ -215,59 +215,59 @@ public class ContainerLifeCycleTest a0.addBean(aa0); dump = trim(a0.dump()); dump = check(dump, "ContainerLifeCycl"); - dump = check(dump, "+?ContainerLife"); + dump = check(dump, "+? ContainerLife"); ContainerLifeCycle aa1 = new ContainerLifeCycle(); a0.addBean(aa1); dump = trim(a0.dump()); dump = check(dump, "ContainerLifeCycl"); - dump = check(dump, "+?ContainerLife"); - dump = check(dump, "+?ContainerLife"); + dump = check(dump, "+? ContainerLife"); + dump = check(dump, "+? ContainerLife"); dump = check(dump, ""); ContainerLifeCycle aa2 = new ContainerLifeCycle(); a0.addBean(aa2, false); dump = trim(a0.dump()); dump = check(dump, "ContainerLifeCycl"); - dump = check(dump, "+?ContainerLife"); - dump = check(dump, "+?ContainerLife"); - dump = check(dump, "+~ContainerLife"); + dump = check(dump, "+? ContainerLife"); + dump = check(dump, "+? ContainerLife"); + dump = check(dump, "+~ ContainerLife"); dump = check(dump, ""); aa1.start(); a0.start(); dump = trim(a0.dump()); dump = check(dump, "ContainerLifeCycl"); - dump = check(dump, "+=ContainerLife"); - dump = check(dump, "+~ContainerLife"); - dump = check(dump, "+~ContainerLife"); + dump = check(dump, "+= ContainerLife"); + dump = check(dump, "+~ ContainerLife"); + dump = check(dump, "+~ ContainerLife"); dump = check(dump, ""); a0.manage(aa1); a0.removeBean(aa2); dump = trim(a0.dump()); dump = check(dump, "ContainerLifeCycl"); - dump = check(dump, "+=ContainerLife"); - dump = check(dump, "+=ContainerLife"); + dump = check(dump, "+= ContainerLife"); + dump = check(dump, "+= ContainerLife"); dump = check(dump, ""); ContainerLifeCycle aaa0 = new ContainerLifeCycle(); aa0.addBean(aaa0); dump = trim(a0.dump()); dump = check(dump, "ContainerLifeCycl"); - dump = check(dump, "+=ContainerLife"); - dump = check(dump, "| +~Container"); - dump = check(dump, "+=ContainerLife"); + dump = check(dump, "+= ContainerLife"); + dump = check(dump, "| +~ Container"); + dump = check(dump, "+= ContainerLife"); dump = check(dump, ""); ContainerLifeCycle aa10 = new ContainerLifeCycle(); aa1.addBean(aa10, true); dump = trim(a0.dump()); dump = check(dump, "ContainerLifeCycl"); - dump = check(dump, "+=ContainerLife"); - dump = check(dump, "| +~Container"); - dump = check(dump, "+=ContainerLife"); - dump = check(dump, " +=Container"); + dump = check(dump, "+= ContainerLife"); + dump = check(dump, "| +~ Container"); + dump = check(dump, "+= ContainerLife"); + dump = check(dump, " += Container"); dump = check(dump, ""); final ContainerLifeCycle a1 = new ContainerLifeCycle(); @@ -288,62 +288,62 @@ public class ContainerLifeCycleTest dump = trim(a0.dump()); dump = check(dump, "ContainerLifeCycl"); - dump = check(dump, "+=ContainerLife"); - dump = check(dump, "| +~Container"); - dump = check(dump, "+=ContainerLife"); - dump = check(dump, "| +=Container"); - dump = check(dump, "+=ContainerLife"); - dump = check(dump, " +>java.util.Arrays$ArrayList"); - dump = check(dump, " | +:ContainerLifeCycle"); - dump = check(dump, " | +:ContainerLifeCycle"); - dump = check(dump, " +>java.util.Arrays$ArrayList"); - dump = check(dump, " +:ContainerLifeCycle"); - dump = check(dump, " +:ContainerLifeCycle"); + dump = check(dump, "+= ContainerLife"); + dump = check(dump, "| +~ Container"); + dump = check(dump, "+= ContainerLife"); + dump = check(dump, "| += Container"); + dump = check(dump, "+= ContainerLife"); + dump = check(dump, " +> java.util.Arrays$ArrayList"); + dump = check(dump, " | +: ContainerLifeCycle"); + dump = check(dump, " | +: ContainerLifeCycle"); + dump = check(dump, " +> java.util.Arrays$ArrayList"); + dump = check(dump, " +: ContainerLifeCycle"); + dump = check(dump, " +: ContainerLifeCycle"); dump = check(dump, ""); a2.addBean(aa0, true); dump = trim(a0.dump()); dump = check(dump, "ContainerLifeCycl"); - dump = check(dump, "+=ContainerLife"); - dump = check(dump, "| +~Container"); - dump = check(dump, "+=ContainerLife"); - dump = check(dump, "| +=Container"); - dump = check(dump, "+=ContainerLife"); - dump = check(dump, " +>java.util.Arrays$ArrayList"); - dump = check(dump, " | +:ContainerLifeCycle"); - dump = check(dump, " | +:ContainerLifeCycle"); - dump = check(dump, " | +=Conta"); - dump = check(dump, " | +~C"); - dump = check(dump, " +>java.util.Arrays$ArrayList"); - dump = check(dump, " +:ContainerLifeCycle"); - dump = check(dump, " +:ContainerLifeCycle"); + dump = check(dump, "+= ContainerLife"); + dump = check(dump, "| +~ Container"); + dump = check(dump, "+= ContainerLife"); + dump = check(dump, "| += Container"); + dump = check(dump, "+= ContainerLife"); + dump = check(dump, " +> java.util.Arrays$ArrayList"); + dump = check(dump, " | +: ContainerLifeCycle"); + dump = check(dump, " | +: ContainerLifeCycle"); + dump = check(dump, " | += Conta"); + dump = check(dump, " | +~ C"); + dump = check(dump, " +> java.util.Arrays$ArrayList"); + dump = check(dump, " +: ContainerLifeCycle"); + dump = check(dump, " +: ContainerLifeCycle"); dump = check(dump, ""); a2.unmanage(aa0); dump = trim(a0.dump()); dump = check(dump, "ContainerLifeCycl"); - dump = check(dump, "+=ContainerLife"); - dump = check(dump, "| +~Container"); - dump = check(dump, "+=ContainerLife"); - dump = check(dump, "| +=Container"); - dump = check(dump, "+=ContainerLife"); - dump = check(dump, " +>java.util.Arrays$ArrayList"); - dump = check(dump, " | +:ContainerLifeCycle"); - dump = check(dump, " | +:ContainerLifeCycle"); - dump = check(dump, " | +~Conta"); - dump = check(dump, " +>java.util.Arrays$ArrayList"); - dump = check(dump, " +:ContainerLifeCycle"); - dump = check(dump, " +:ContainerLifeCycle"); + dump = check(dump, "+= ContainerLife"); + dump = check(dump, "| +~ Container"); + dump = check(dump, "+= ContainerLife"); + dump = check(dump, "| += Container"); + dump = check(dump, "+= ContainerLife"); + dump = check(dump, " +> java.util.Arrays$ArrayList"); + dump = check(dump, " | +: ContainerLifeCycle"); + dump = check(dump, " | +: ContainerLifeCycle"); + dump = check(dump, " | +~ Conta"); + dump = check(dump, " +> java.util.Arrays$ArrayList"); + dump = check(dump, " +: ContainerLifeCycle"); + dump = check(dump, " +: ContainerLifeCycle"); dump = check(dump, ""); a0.unmanage(aa); dump = trim(a0.dump()); dump = check(dump, "ContainerLifeCycl"); - dump = check(dump, "+=ContainerLife"); - dump = check(dump, "| +~Container"); - dump = check(dump, "+=ContainerLife"); - dump = check(dump, "| +=Container"); - dump = check(dump, "+~ContainerLife"); + dump = check(dump, "+= ContainerLife"); + dump = check(dump, "| +~ Container"); + dump = check(dump, "+= ContainerLife"); + dump = check(dump, "| += Container"); + dump = check(dump, "+~ ContainerLife"); dump = check(dump, ""); } From 00e2026594fdaae55b84a94d6701ce2066a1a250 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Sat, 24 Nov 2018 21:16:19 +0100 Subject: [PATCH 07/10] Fix conscrypt version for osgi jdk8 Signed-off-by: Greg Wilkins --- jetty-osgi/test-jetty-osgi/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-osgi/test-jetty-osgi/pom.xml b/jetty-osgi/test-jetty-osgi/pom.xml index e90e480bd90..c425d5208fe 100644 --- a/jetty-osgi/test-jetty-osgi/pom.xml +++ b/jetty-osgi/test-jetty-osgi/pom.xml @@ -546,7 +546,7 @@ - -Dmortbay-alpn-boot=${settings.localRepository}/org/mortbay/jetty/alpn/alpn-boot/${alpn.version}/alpn-boot-${alpn.version}.jar + -Dmortbay-alpn-boot=${settings.localRepository}/org/mortbay/jetty/alpn/alpn-boot/${alpn.version}/alpn-boot-${alpn.version}.jar -Dconscrypt-version=${conscrypt.version} From f5445a759e798d908118c2191487e278cebd3b08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Nawrocki?= Date: Tue, 27 Nov 2018 01:57:28 +0100 Subject: [PATCH 08/10] Fixed invalid QoS Filter parameter names (#3149) Signed-off-by: Pawel Nawrocki --- .../src/main/asciidoc/administration/extras/qos-filter.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jetty-documentation/src/main/asciidoc/administration/extras/qos-filter.adoc b/jetty-documentation/src/main/asciidoc/administration/extras/qos-filter.adoc index 93bef25415d..2b6eccb0735 100644 --- a/jetty-documentation/src/main/asciidoc/administration/extras/qos-filter.adoc +++ b/jetty-documentation/src/main/asciidoc/administration/extras/qos-filter.adoc @@ -110,11 +110,11 @@ The maximum number of requests to be serviced at a time. The default is 10. maxPriority:: The maximum valid priority that can be assigned to a request. A request with a high priority value is more important than a request with a low priority value. The default is 10. -waitMS:: +waitMs:: The length of time, in milliseconds, to wait while trying to accept a new request. Used when the maxRequests limit is reached. Default is 50 ms. -suspendMS:: +suspendMs:: Length of time, in milliseconds, that the request will be suspended if it is not accepted immediately. If not set, the container's default suspend period applies. Default is -1 ms. managedAttr:: From 9ee57192dc23c1b7c3b1e80fd786fa6cf822e856 Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Wed, 28 Nov 2018 10:12:07 +1000 Subject: [PATCH 09/10] use withMaven junit publisher (#3150) Signed-off-by: olivier lamy --- Jenkinsfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 1792f665999..cc9bec25fd4 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -10,7 +10,6 @@ pipeline { options { timeout(time: 120, unit: 'MINUTES') } steps { mavenBuild("jdk8", "-Pmongodb install") - junit '**/target/surefire-reports/TEST-*.xml,**/target/failsafe-reports/TEST-*.xml' // Collect up the jacoco execution results (only on main build) jacoco inclusionPattern: '**/org/eclipse/jetty/**/*.class', exclusionPattern: '' + @@ -95,6 +94,7 @@ def mavenBuild(jdk, cmdline) { jdk: "$jdk", publisherStrategy: 'EXPLICIT', globalMavenSettingsConfig: settingsName, + options: [junitPublisher(disabled: false)], mavenOpts: mavenOpts, mavenLocalRepo: localRepo) { // Some common Maven command line + provided command line @@ -102,4 +102,4 @@ def mavenBuild(jdk, cmdline) { } } -// vim: et:ts=2:sw=2:ft=groovy \ No newline at end of file +// vim: et:ts=2:sw=2:ft=groovy From bee1487eccc135d98bcf88b56d9af2bed4dff7fc Mon Sep 17 00:00:00 2001 From: Olivier Lamy Date: Wed, 28 Nov 2018 10:13:04 +1000 Subject: [PATCH 10/10] copying base resources must happen when preparing packaging we must not rely on id (#3138) Signed-off-by: olivier lamy --- jetty-distribution/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-distribution/pom.xml b/jetty-distribution/pom.xml index 05976455701..e83ad110f1f 100644 --- a/jetty-distribution/pom.xml +++ b/jetty-distribution/pom.xml @@ -29,7 +29,7 @@ copy-base-assembly-tree - package + prepare-package copy-resources