This is an automated email from the ASF dual-hosted git repository.

afs pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git

commit 05a06f44631f3a0d7ea8ab449c70f066ebbbfe6b
Author: Andy Seaborne <[email protected]>
AuthorDate: Fri Jul 3 17:05:11 2026 +0100

    GH-4033: Disable sessions
---
 .../apache/jena/fuseki/mod/shiro/FMod_Shiro.java   | 109 +++++++++++----------
 .../apache/jena/fuseki/mod/shiro/FusekiShiro.java  |   8 +-
 ...a => FusekiShiroEnvironmentLoaderListener.java} |   8 +-
 .../jena/fuseki/mod/shiro/FusekiShiroFilter.java   |  74 ++++++++++++++
 4 files changed, 142 insertions(+), 57 deletions(-)

diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FMod_Shiro.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FMod_Shiro.java
index 675a4b09d6..da8acf965e 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FMod_Shiro.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FMod_Shiro.java
@@ -26,23 +26,23 @@ import java.util.List;
 import java.util.Set;
 
 import jakarta.servlet.Filter;
-import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpSessionEvent;
+import jakarta.servlet.http.HttpSessionListener;
 import org.apache.jena.atlas.io.IOX;
 import org.apache.jena.atlas.lib.Lib;
 import org.apache.jena.atlas.logging.FmtLog;
+import org.apache.jena.atlas.logging.Log;
 import org.apache.jena.cmd.ArgDecl;
 import org.apache.jena.cmd.CmdException;
 import org.apache.jena.cmd.CmdGeneral;
 import org.apache.jena.fuseki.Fuseki;
 import org.apache.jena.fuseki.FusekiConfigException;
-import org.apache.jena.fuseki.FusekiException;
 import org.apache.jena.fuseki.main.FusekiServer;
 import org.apache.jena.fuseki.main.runner.ServerArgs;
 import org.apache.jena.fuseki.main.sys.FusekiModule;
 import org.apache.jena.fuseki.mgt.FusekiServerCtl;
 import org.apache.jena.rdf.model.Model;
 import org.apache.shiro.lang.io.ResourceUtils;
-import org.apache.shiro.web.servlet.ShiroFilter;
 import org.eclipse.jetty.ee11.servlet.ServletContextHandler;
 import org.eclipse.jetty.ee11.servlet.SessionHandler;
 import org.slf4j.Logger;
@@ -77,12 +77,25 @@ public class FMod_Shiro implements FusekiModule {
     /** Shiro configuration file given a a command line argument. */
     private String argShiroFile = null;
 
+    /** Control for whether to have Jetty/Jakarta sessions,
+     * which are then used by Shiro.
+     * But they don't get cleared up and keep hold of RAM.
+     * See <a href="https://github.com/apache/jena/issues/4033";>GH-4033</a>.
+     * <p>
+     * If {@code true}, then disable Shiro session creation on every request,
+     * and don't create a Jetty sever session manager.
+     * <p>
+     * It the future, it is likely that this flag is dropped
+     * and the code support only "disabled sessions".
+     */
+    private static final boolean DISABLE_SESSIONS = false;
+
     public FMod_Shiro() {}
 
     // ---- If used from the command line
     @Override
     public void serverArgsModify(CmdGeneral fusekiCmd, ServerArgs serverArgs) {
-        fusekiCmd.add(argShiroIni);
+        fusekiCmd.add(argShiroIni, "--shiro", "Set the shiro.ini file");
         argShiroFile = null;
     }
 
@@ -117,7 +130,7 @@ public class FMod_Shiro implements FusekiModule {
         if ( shiroConfig != null )
             return shiroConfig;
 
-        // Environment variable:  FUSEKI_SHIRO
+        // Environment variable: FUSEKI_SHIRO
         shiroConfig = Lib.getenv(FusekiServerCtl.envFusekiShiro);
         if ( shiroConfig != null )
             return shiroConfig;
@@ -144,83 +157,73 @@ public class FMod_Shiro implements FusekiModule {
         if ( shiroConfig == null )
             return;
 
-        // Shiro prints stack traces and exceptions. Rather than silence
-        //checkShiroConfiguration(shiroConfig);
-
         // Shiro-style.
         String shiroResourceName = FusekiShiro.withResourcePrefix(shiroConfig);
         if ( ! ResourceUtils.resourceExists(shiroResourceName) )
             throw new FusekiConfigException("Shiro resource does not exist");
-        Filter filter = new FusekiShiroFilter(shiroResourceName);
+
+        Filter filter = new FusekiShiroFilter(shiroResourceName, 
DISABLE_SESSIONS);
         // This is a "before" filter.
         serverBuilder.addFilter("/*", filter);
         serverBuilder.setServletAttribute(Fuseki.attrShiroResource, 
shiroResourceName);
-
         // Clear.
         this.argShiroFile = null;
     }
 
-    /**
-     * FusekiShiroFilter, includes Shiro initialization. Fuseki is a
-     * not a webapp so it needs to trigger off servlet initialization.
-     */
-    private static class FusekiShiroFilter extends ShiroFilter {
-
-        private final String shiroInitializationResource;
-
-        FusekiShiroFilter(String shiroResourceName) {
-            // Shiro file -- URLs are "file:<no encoding>"
-            shiroInitializationResource = shiroResourceName;
-        }
-
-        @Override
-        public void init() throws Exception {
-            try {
-                // Intercept Shiro initialization.
-                List<String> locations = List.of();
-                if ( shiroInitializationResource != null ) {
-                    locations = List.of(shiroInitializationResource);
-                }
-                FusekiShiro.shiroEnvironment(getServletContext(), locations);
-            } catch (FusekiException ex) {
-                // Wrap so ShiroFilter does not log it.
-                throw new ServletException(ex);
-            }
-            super.init();
-        }
-    }
-
     @Override
     public void serverBeforeStarting(FusekiServer server) {
         try {
             String x 
=(String)server.getServletContext().getAttribute(Fuseki.attrShiroResource);
             if ( x != null )
                 FmtLog.info(shiroConfigLog, "Shiro configuration: %s", x);
-//            else
-//                FmtLog.info(shiroConfigLog, "No Shiro configuration");
+            //            else
+            //                FmtLog.info(shiroConfigLog, "No Shiro 
configuration");
         } catch (ClassCastException ex) {
             FmtLog.warn(shiroConfigLog, "Unexpected Shiro configuration: %s", 
server.getServletContext().getAttribute(Fuseki.attrShiroResource));
         }
 
-        // Shiro requires a session handler.
-        // This needs the Jetty server to have been created.
-        org.eclipse.jetty.server.Server jettyServer = server.getJettyServer();
-        try {
-            ServletContextHandler servletContextHandler = 
(ServletContextHandler)(jettyServer.getHandler());
-            if ( servletContextHandler.getSessionHandler() == null ) {
-                SessionHandler sessionsHandler = new SessionHandler();
-                servletContextHandler.setHandler(sessionsHandler);
+        if ( ! DISABLE_SESSIONS ) {
+            // If shiro requires a session handler.
+            // This needs the Jetty server to have been created.
+            // Allocate a Jakarta SessionHandler
+            try {
+                org.eclipse.jetty.server.Server jettyServer = 
server.getJettyServer();
+
+                ServletContextHandler servletContextHandler = 
(ServletContextHandler)(jettyServer.getHandler());
+                if ( servletContextHandler.getSessionHandler() == null ) {
+                    SessionHandler sessionHandler = new SessionHandler();
+                    if ( true )
+                        addHttpSessionListener(sessionHandler);
+                    servletContextHandler.setHandler(sessionHandler);
+                }
+            } catch (RuntimeException ex) {
+                shiroConfigLog.error("Failed to set a session manager - server 
aborted");
+                throw ex;
             }
-        } catch (RuntimeException ex) {
-            shiroConfigLog.error("Failed to set a session manager - server 
aborted");
-            throw ex;
         }
     }
 
+
     // Later:
     // Reload shirio.ini file and reset.
 //    // Currently, no actual - the server admin area does not move during the 
run of a server.
 //    /** {@inheritDoc} */
 //    @Override
 //    public void serverReload(FusekiServer server) { }
+
+
+    private static void addHttpSessionListener( SessionHandler sessionHandler) 
{
+        // Development
+        HttpSessionListener httpSessionListener = new HttpSessionListener() {
+            @Override
+            public void sessionCreated(HttpSessionEvent se) {
+                Log.info(Fuseki.serverLog, "Jakarta session created");
+            }
+            @Override
+            public void sessionDestroyed(HttpSessionEvent se) {
+                Log.info(Fuseki.serverLog, "Jakarta session destroyed");
+            }
+        };
+        sessionHandler.addEventListener(httpSessionListener);
+    }
 }
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FusekiShiro.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FusekiShiro.java
index 1b6424db40..2d418c859e 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FusekiShiro.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FusekiShiro.java
@@ -39,7 +39,7 @@ import org.slf4j.LoggerFactory;
 
     static void shiroEnvironment(ServletContext servletContext, List<String> 
possibleShiroIniFiles) {
         // Shiro environment initialization, done here because we don't have 
webapp listeners.
-        EnvironmentLoaderListener shiroListener = new 
ShiroEnvironmentLoaderListener(possibleShiroIniFiles);
+        EnvironmentLoaderListener shiroListener = new 
FusekiShiroEnvironmentLoaderListener(possibleShiroIniFiles);
         // Silence these - handle the errors ourselves.
         LogCtl.disable(org.apache.shiro.web.env.IniWebEnvironment.class);
         LogCtl.disable(org.apache.shiro.web.env.EnvironmentLoader.class);
@@ -83,6 +83,9 @@ import org.slf4j.LoggerFactory;
         return null;
     }
 
+    /**
+     * Ensure a filename is formatted the way SHiro wants it.
+     */
     public static String withResourcePrefix(String shiroFileName) {
         if ( shiroFileName.startsWith(fileShiroPrefix) )
             return shiroFileName;
@@ -90,6 +93,9 @@ import org.slf4j.LoggerFactory;
         return fileShiroPrefix+shiroFileName;
     }
 
+    /**
+     * Reverse formatting a file mae. See {@link #withResourcePrefix}.
+     */
     public static String removeResourcePrefix(String shiroFileResource) {
         if ( shiroFileResource.startsWith(fileShiroPrefix) ) {
             // Shiro format resource name.
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/ShiroEnvironmentLoaderListener.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FusekiShiroEnvironmentLoaderListener.java
similarity index 92%
rename from 
jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/ShiroEnvironmentLoaderListener.java
rename to 
jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FusekiShiroEnvironmentLoaderListener.java
index 514798467d..72c02e2827 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/ShiroEnvironmentLoaderListener.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FusekiShiroEnvironmentLoaderListener.java
@@ -34,11 +34,11 @@ import org.apache.shiro.web.env.WebEnvironment;
  * locations for a {@code shiro.ini} file. It will return the first found in a 
list
  * of possible file names.
  */
-class ShiroEnvironmentLoaderListener extends EnvironmentLoaderListener{
+class FusekiShiroEnvironmentLoaderListener extends EnvironmentLoaderListener{
 
     private List<String> locations;
 
-    /*package*/ ShiroEnvironmentLoaderListener(List<String> locations) {
+    /*package*/ FusekiShiroEnvironmentLoaderListener(List<String> locations) {
         this.locations = locations;
     }
 
@@ -51,8 +51,10 @@ class ShiroEnvironmentLoaderListener extends 
EnvironmentLoaderListener{
      */
     @Override
     protected void customizeEnvironment(WebEnvironment environment) {
-        if ( locations == null )
+        if ( locations == null ) {
+            super.customizeEnvironment(environment);
             return;
+        }
 
         // Look for shiro.ini
         if ( environment instanceof ResourceBasedWebEnvironment ) {
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FusekiShiroFilter.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FusekiShiroFilter.java
new file mode 100644
index 0000000000..dbcd5fe984
--- /dev/null
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FusekiShiroFilter.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *   SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.apache.jena.fuseki.mod.shiro;
+
+import java.util.List;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import org.apache.jena.fuseki.FusekiException;
+import org.apache.shiro.subject.support.DefaultSubjectContext;
+import org.apache.shiro.web.servlet.ShiroFilter;
+
+/**
+ * FusekiShiroFilter, includes Shiro initialization. Fuseki is a
+ * not a webapp so it needs to trigger off servlet initialization.
+ */
+/*package*/ class FusekiShiroFilter extends ShiroFilter {
+
+    private final String shiroInitializationResource;
+    private final boolean disableSessions;
+
+    /*package*/ FusekiShiroFilter(String shiroResourceName, boolean 
disableSessions) {
+        // Shiro file -- URLs are "file:<no encoding>"
+        this.shiroInitializationResource = shiroResourceName;
+        this.disableSessions = disableSessions;
+    }
+
+    @Override
+    public void init() throws Exception {
+        try {
+            // Intercept Shiro initialization.
+            List<String> locations = List.of();
+            if ( shiroInitializationResource != null ) {
+                locations = List.of(shiroInitializationResource);
+            }
+            FusekiShiro.shiroEnvironment(getServletContext(), locations);
+        } catch (FusekiException ex) {
+            // Wrap so ShiroFilter does not log it.
+            throw new ServletException(ex);
+        }
+        super.init();
+    }
+
+    @Override
+    protected ServletRequest prepareServletRequest(ServletRequest request, 
ServletResponse response, FilterChain chain) {
+        if ( disableSessions ) {
+            // GH-4033 : https://github.com/apache/jena/issues/4033
+            // See NoSessionCreationFilter
+            
request.setAttribute(DefaultSubjectContext.SESSION_CREATION_ENABLED, 
Boolean.FALSE);
+        }
+        return super.prepareServletRequest(request, response, chain);
+    }
+}
\ No newline at end of file

Reply via email to