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
