diff --git a/core/src/main/java/lucee/runtime/PageContextImpl.java b/core/src/main/java/lucee/runtime/PageContextImpl.java
index d401313be8..204fa2be2b 100644
--- a/core/src/main/java/lucee/runtime/PageContextImpl.java
+++ b/core/src/main/java/lucee/runtime/PageContextImpl.java
@@ -3998,7 +3998,8 @@ public ClassLoader getRPCClassLoader(boolean reload, JavaSettings customJS) thro
public void resetSession() {
if (this.session != null && this.session instanceof JSession) {
- getSession().invalidate();
+ HttpSession httpSession = getHttpServletRequest().getSession(false);
+ if (httpSession != null) httpSession.invalidate();
}
this.session = null;
}
diff --git a/core/src/main/java/lucee/runtime/type/scope/ScopeContext.java b/core/src/main/java/lucee/runtime/type/scope/ScopeContext.java
index 9ebafd472b..579405d4cb 100755
--- a/core/src/main/java/lucee/runtime/type/scope/ScopeContext.java
+++ b/core/src/main/java/lucee/runtime/type/scope/ScopeContext.java
@@ -963,7 +963,7 @@ public void invalidateUserScope(PageContextImpl pc, boolean migrateSessionData,
if (hasSessionManagement) {
if (isJ2EESession) {
// For J2EE sessions, try the HttpSession attribute first
- HttpSession httpSession = pc.getSession();
+ HttpSession httpSession = pc.getHttpServletRequest().getSession(false);
if (httpSession != null) {
Object session = httpSession.getAttribute(appContext.getName());
if (session instanceof JSession) {
@@ -999,7 +999,7 @@ public void invalidateUserScope(PageContextImpl pc, boolean migrateSessionData,
// For J2EE sessions, handle the servlet container's session (JSESSIONID)
if (isJ2EESession && hasSessionManagement) {
- HttpSession httpSession = pc.getSession();
+ HttpSession httpSession = pc.getHttpServletRequest().getSession(false);
if (httpSession != null) {
if (migrateSessionData) {
// sessionRotate: rotate to a new session ID but keep session alive
@@ -1016,21 +1016,26 @@ public void invalidateUserScope(PageContextImpl pc, boolean migrateSessionData,
// For J2EE sessionRotate with a real httpSession (Tomcat), don't reset session - we already called
// changeSessionId() and want to keep the data
// But for JSR-223 (where httpSession is null), we need to reset to create a new session
- HttpSession httpSessionForReset = pc.getSession();
+ HttpSession httpSessionForReset = pc.getHttpServletRequest().getSession(false);
if (!(isJ2EESession && migrateSessionData && httpSessionForReset != null)) {
pc.resetSession();
}
pc.resetClient();
if (oldSession != null) {
- UserScope newSession;
- if (isJ2EESession) {
- newSession = getSessionScope(pc);
+ if (migrateSessionData) {
+ UserScope newSession;
+ if (isJ2EESession) {
+ newSession = getSessionScope(pc);
+ }
+ else {
+ newSession = (UserScope) getCFScope(pc, true, Scope.SCOPE_SESSION);
+ }
+ migrate(pc, oldSession, newSession, migrateSessionData);
}
else {
- newSession = (UserScope) getCFScope(pc, true, Scope.SCOPE_SESSION);
+ oldSession.clear();
}
- migrate(pc, oldSession, newSession, migrateSessionData);
}
if (oldClient != null) migrate(pc, oldClient, (UserScope) getCFScope(pc, true, Scope.SCOPE_CLIENT), migrateClientData);
@@ -1063,4 +1068,4 @@ else if (newScope instanceof JSession && oldScope instanceof JSession) {
}
}
-}
\ No newline at end of file
+}
diff --git a/test/tickets/LDEV4166.cfc b/test/tickets/LDEV4166.cfc
index 65e3846e76..0e0df97a79 100644
--- a/test/tickets/LDEV4166.cfc
+++ b/test/tickets/LDEV4166.cfc
@@ -22,16 +22,6 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="session" {
var appName = listFirst( trim( cfmlSessionId.filecontent ), '-' ) & "-" ;
- // allow session to expire
- expect( getSessionCount( appName ) ).toBe( 1 );
-
- sleep(1001);
- admin
- action="purgeExpiredSessions"
- type="server"
- password="#request.SERVERADMINPASSWORD#";
- //systemOutput(server.LDEV4166_ended_CFML_Sessions, true);
- // let's check first that the session actually ended!
expect( getSessionCount( appName ) ).toBe( 0 );
expect( structKeyExists( server.LDEV4166_ended_CFML_Sessions, trim( cfmlSessionId.filecontent ) ) ).toBeTrue();
});
@@ -46,15 +36,6 @@ component extends="org.lucee.cfml.test.LuceeTestCase" labels="session" {
var appName = listFirst( trim( j2eeSessionId.filecontent ), '-' ) & "-" ;
- expect( getSessionCount( appName ) ).toBe( 1 );
- // allow session to expire
- sleep(1001);
- admin
- action="purgeExpiredSessions"
- type="server"
- password="#request.SERVERADMINPASSWORD#";
- //systemOutput(server.LDEV4166_ended_JEE_Sessions, true);
- // let's check first that the session actually ended!
expect( getSessionCount( appName ) ).toBe( 0 );
expect( structKeyExists( server.LDEV4166_ended_JEE_Sessions, trim( j2eeSessionId.filecontent ) ) ).toBeTrue();
});
diff --git a/test/tickets/LDEV6447.cfc b/test/tickets/LDEV6447.cfc
new file mode 100644
index 0000000000..3c765a9d24
--- /dev/null
+++ b/test/tickets/LDEV6447.cfc
@@ -0,0 +1,44 @@
+component extends="org.lucee.cfml.test.LuceeTestCase" labels="session" {
+
+ function run( testResults, testBox ) {
+ describe( "LDEV-6447: sessionInvalidate() JEE session lifecycle", function() {
+ it( title="does not eagerly create a replacement JEE session", skip=isJsr223(), body=function( currentSpec ) {
+ var data = request( "invalidate-before-commit.cfm" );
+ expect( data.success ).toBeTrue( data.stacktrace ?: "no stacktrace" );
+ expect( data.oldSessionInvalidated ).toBeTrue( "The existing HttpSession should be invalidated" );
+ expect( data.replacementSessionExists ).toBeFalse( "sessionInvalidate() should not eagerly create a replacement HttpSession" );
+ expect( data.sessionCreatedAfterAccess ).toBeTrue( "Accessing SESSION should lazily create a new HttpSession" );
+ expect( data.oldSessionId ).notToBe( data.newSessionId, "The lazily-created session should have a new ID" );
+ });
+
+ it( title="invalidates an existing JEE session", skip=isJsr223(), body=function( currentSpec ) {
+ var data = request( "invalidate-after-commit.cfm" );
+ expect( data.success ).toBeTrue( data.stacktrace ?: "no stacktrace" );
+ expect( data.sessionInvalidated ).toBeTrue( "The existing HttpSession should be invalidated" );
+ });
+
+ it( title="is a no-op when no JEE session exists", skip=isJsr223(), body=function( currentSpec ) {
+ var data = request( "invalidate-after-commit-without-session.cfm" );
+ expect( data.success ).toBeTrue( data.stacktrace ?: "no stacktrace" );
+ });
+
+ });
+ }
+
+ private boolean function isJsr223() {
+ return cgi.request_url == "http://localhost/index.cfm";
+ }
+
+ private struct function request( required string template ) {
+ var hostIdx = find( cgi.script_name, cgi.request_url );
+ if ( hostIdx <= 0 ) {
+ throw "Failed to extract host from CGI values";
+ }
+
+ var result = "";
+ http method="get"
+ url="#left( cgi.request_url, hostIdx - 1 )#/test/tickets/LDEV6447/jee-session/#template#"
+ result="result";
+ return deserializeJSON( result.filecontent );
+ }
+}
diff --git a/test/tickets/LDEV6447/jee-session/Application.cfc b/test/tickets/LDEV6447/jee-session/Application.cfc
new file mode 100644
index 0000000000..be79d7437e
--- /dev/null
+++ b/test/tickets/LDEV6447/jee-session/Application.cfc
@@ -0,0 +1,9 @@
+component {
+ this.name = "ldev6447_jee_session_invalidate";
+ this.sessionManagement = true;
+ this.sessionStorage = "memory";
+ this.sessionTimeout = createTimeSpan( 0, 0, 0, 30 );
+ this.setClientCookies = true;
+ this.applicationTimeout = createTimeSpan( 0, 0, 1, 0 );
+ this.sessionType = "jee";
+}
diff --git a/test/tickets/LDEV6447/jee-session/invalidate-after-commit-without-session.cfm b/test/tickets/LDEV6447/jee-session/invalidate-after-commit-without-session.cfm
new file mode 100644
index 0000000000..a36486708f
--- /dev/null
+++ b/test/tickets/LDEV6447/jee-session/invalidate-after-commit-without-session.cfm
@@ -0,0 +1,20 @@
+
+ result = {
+ success: true,
+ stacktrace: ""
+ };
+
+ sessionInvalidate();
+ content type="application/json";
+ getPageContext().getResponse().flushBuffer();
+
+ try {
+ sessionInvalidate();
+ }
+ catch ( any e ) {
+ result.success = false;
+ result.stacktrace = e.stacktrace;
+ }
+
+ echo( serializeJSON( result ) );
+
diff --git a/test/tickets/LDEV6447/jee-session/invalidate-after-commit.cfm b/test/tickets/LDEV6447/jee-session/invalidate-after-commit.cfm
new file mode 100644
index 0000000000..6717963b70
--- /dev/null
+++ b/test/tickets/LDEV6447/jee-session/invalidate-after-commit.cfm
@@ -0,0 +1,27 @@
+
+ result = {
+ success: true,
+ stacktrace: "",
+ sessionInvalidated: false
+ };
+
+ httpSession = getPageContext().getSession();
+ content type="application/json";
+ getPageContext().getResponse().flushBuffer();
+
+ try {
+ sessionInvalidate();
+ try {
+ httpSession.getAttribute( "test" );
+ }
+ catch ( any e ) {
+ result.sessionInvalidated = true;
+ }
+ }
+ catch ( any e ) {
+ result.success = false;
+ result.stacktrace = e.stacktrace;
+ }
+
+ echo( serializeJSON( result ) );
+
diff --git a/test/tickets/LDEV6447/jee-session/invalidate-before-commit.cfm b/test/tickets/LDEV6447/jee-session/invalidate-before-commit.cfm
new file mode 100644
index 0000000000..f104a1951c
--- /dev/null
+++ b/test/tickets/LDEV6447/jee-session/invalidate-before-commit.cfm
@@ -0,0 +1,36 @@
+
+ result = {
+ success: true,
+ stacktrace: "",
+ oldSessionInvalidated: false,
+ replacementSessionExists: true,
+ sessionCreatedAfterAccess: false,
+ oldSessionId: "",
+ newSessionId: ""
+ };
+
+ try {
+ httpSession = getPageContext().getSession();
+ result.oldSessionId = httpSession.getId();
+
+ sessionInvalidate();
+ result.replacementSessionExists = !isNull( getPageContext().getRequest().getSession( false ) );
+
+ try {
+ httpSession.getAttribute( "test" );
+ }
+ catch ( any e ) {
+ result.oldSessionInvalidated = true;
+ }
+
+ result.newSessionId = session.sessionid;
+ result.sessionCreatedAfterAccess = !isNull( getPageContext().getRequest().getSession( false ) );
+ }
+ catch ( any e ) {
+ result.success = false;
+ result.stacktrace = e.stacktrace;
+ }
+
+ content type="application/json";
+ echo( serializeJSON( result ) );
+