Skip to content

Commit 05a06f4

Browse files
committed
GH-4033: Disable sessions
1 parent 8d40f86 commit 05a06f4

4 files changed

Lines changed: 142 additions & 57 deletions

File tree

jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FMod_Shiro.java

Lines changed: 56 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,23 @@
2626
import java.util.Set;
2727

2828
import jakarta.servlet.Filter;
29-
import jakarta.servlet.ServletException;
29+
import jakarta.servlet.http.HttpSessionEvent;
30+
import jakarta.servlet.http.HttpSessionListener;
3031
import org.apache.jena.atlas.io.IOX;
3132
import org.apache.jena.atlas.lib.Lib;
3233
import org.apache.jena.atlas.logging.FmtLog;
34+
import org.apache.jena.atlas.logging.Log;
3335
import org.apache.jena.cmd.ArgDecl;
3436
import org.apache.jena.cmd.CmdException;
3537
import org.apache.jena.cmd.CmdGeneral;
3638
import org.apache.jena.fuseki.Fuseki;
3739
import org.apache.jena.fuseki.FusekiConfigException;
38-
import org.apache.jena.fuseki.FusekiException;
3940
import org.apache.jena.fuseki.main.FusekiServer;
4041
import org.apache.jena.fuseki.main.runner.ServerArgs;
4142
import org.apache.jena.fuseki.main.sys.FusekiModule;
4243
import org.apache.jena.fuseki.mgt.FusekiServerCtl;
4344
import org.apache.jena.rdf.model.Model;
4445
import org.apache.shiro.lang.io.ResourceUtils;
45-
import org.apache.shiro.web.servlet.ShiroFilter;
4646
import org.eclipse.jetty.ee11.servlet.ServletContextHandler;
4747
import org.eclipse.jetty.ee11.servlet.SessionHandler;
4848
import org.slf4j.Logger;
@@ -77,12 +77,25 @@ public static FMod_Shiro create() {
7777
/** Shiro configuration file given a a command line argument. */
7878
private String argShiroFile = null;
7979

80+
/** Control for whether to have Jetty/Jakarta sessions,
81+
* which are then used by Shiro.
82+
* But they don't get cleared up and keep hold of RAM.
83+
* See <a href="https://github.com/apache/jena/issues/4033">GH-4033</a>.
84+
* <p>
85+
* If {@code true}, then disable Shiro session creation on every request,
86+
* and don't create a Jetty sever session manager.
87+
* <p>
88+
* It the future, it is likely that this flag is dropped
89+
* and the code support only "disabled sessions".
90+
*/
91+
private static final boolean DISABLE_SESSIONS = false;
92+
8093
public FMod_Shiro() {}
8194

8295
// ---- If used from the command line
8396
@Override
8497
public void serverArgsModify(CmdGeneral fusekiCmd, ServerArgs serverArgs) {
85-
fusekiCmd.add(argShiroIni);
98+
fusekiCmd.add(argShiroIni, "--shiro", "Set the shiro.ini file");
8699
argShiroFile = null;
87100
}
88101

@@ -117,7 +130,7 @@ private String decideShiroConfiguration(FusekiServer.Builder serverBuilder) {
117130
if ( shiroConfig != null )
118131
return shiroConfig;
119132

120-
// Environment variable: FUSEKI_SHIRO
133+
// Environment variable: FUSEKI_SHIRO
121134
shiroConfig = Lib.getenv(FusekiServerCtl.envFusekiShiro);
122135
if ( shiroConfig != null )
123136
return shiroConfig;
@@ -144,83 +157,73 @@ public void prepare(FusekiServer.Builder serverBuilder, Set<String> datasetNames
144157
if ( shiroConfig == null )
145158
return;
146159

147-
// Shiro prints stack traces and exceptions. Rather than silence
148-
//checkShiroConfiguration(shiroConfig);
149-
150160
// Shiro-style.
151161
String shiroResourceName = FusekiShiro.withResourcePrefix(shiroConfig);
152162
if ( ! ResourceUtils.resourceExists(shiroResourceName) )
153163
throw new FusekiConfigException("Shiro resource does not exist");
154-
Filter filter = new FusekiShiroFilter(shiroResourceName);
164+
165+
Filter filter = new FusekiShiroFilter(shiroResourceName, DISABLE_SESSIONS);
155166
// This is a "before" filter.
156167
serverBuilder.addFilter("/*", filter);
157168
serverBuilder.setServletAttribute(Fuseki.attrShiroResource, shiroResourceName);
158-
159169
// Clear.
160170
this.argShiroFile = null;
161171
}
162172

163-
/**
164-
* FusekiShiroFilter, includes Shiro initialization. Fuseki is a
165-
* not a webapp so it needs to trigger off servlet initialization.
166-
*/
167-
private static class FusekiShiroFilter extends ShiroFilter {
168-
169-
private final String shiroInitializationResource;
170-
171-
FusekiShiroFilter(String shiroResourceName) {
172-
// Shiro file -- URLs are "file:<no encoding>"
173-
shiroInitializationResource = shiroResourceName;
174-
}
175-
176-
@Override
177-
public void init() throws Exception {
178-
try {
179-
// Intercept Shiro initialization.
180-
List<String> locations = List.of();
181-
if ( shiroInitializationResource != null ) {
182-
locations = List.of(shiroInitializationResource);
183-
}
184-
FusekiShiro.shiroEnvironment(getServletContext(), locations);
185-
} catch (FusekiException ex) {
186-
// Wrap so ShiroFilter does not log it.
187-
throw new ServletException(ex);
188-
}
189-
super.init();
190-
}
191-
}
192-
193173
@Override
194174
public void serverBeforeStarting(FusekiServer server) {
195175
try {
196176
String x =(String)server.getServletContext().getAttribute(Fuseki.attrShiroResource);
197177
if ( x != null )
198178
FmtLog.info(shiroConfigLog, "Shiro configuration: %s", x);
199-
// else
200-
// FmtLog.info(shiroConfigLog, "No Shiro configuration");
179+
// else
180+
// FmtLog.info(shiroConfigLog, "No Shiro configuration");
201181
} catch (ClassCastException ex) {
202182
FmtLog.warn(shiroConfigLog, "Unexpected Shiro configuration: %s", server.getServletContext().getAttribute(Fuseki.attrShiroResource));
203183
}
204184

205-
// Shiro requires a session handler.
206-
// This needs the Jetty server to have been created.
207-
org.eclipse.jetty.server.Server jettyServer = server.getJettyServer();
208-
try {
209-
ServletContextHandler servletContextHandler = (ServletContextHandler)(jettyServer.getHandler());
210-
if ( servletContextHandler.getSessionHandler() == null ) {
211-
SessionHandler sessionsHandler = new SessionHandler();
212-
servletContextHandler.setHandler(sessionsHandler);
185+
if ( ! DISABLE_SESSIONS ) {
186+
// If shiro requires a session handler.
187+
// This needs the Jetty server to have been created.
188+
// Allocate a Jakarta SessionHandler
189+
try {
190+
org.eclipse.jetty.server.Server jettyServer = server.getJettyServer();
191+
192+
ServletContextHandler servletContextHandler = (ServletContextHandler)(jettyServer.getHandler());
193+
if ( servletContextHandler.getSessionHandler() == null ) {
194+
SessionHandler sessionHandler = new SessionHandler();
195+
if ( true )
196+
addHttpSessionListener(sessionHandler);
197+
servletContextHandler.setHandler(sessionHandler);
198+
}
199+
} catch (RuntimeException ex) {
200+
shiroConfigLog.error("Failed to set a session manager - server aborted");
201+
throw ex;
213202
}
214-
} catch (RuntimeException ex) {
215-
shiroConfigLog.error("Failed to set a session manager - server aborted");
216-
throw ex;
217203
}
218204
}
219205

206+
220207
// Later:
221208
// Reload shirio.ini file and reset.
222209
// // Currently, no actual - the server admin area does not move during the run of a server.
223210
// /** {@inheritDoc} */
224211
// @Override
225212
// public void serverReload(FusekiServer server) { }
213+
214+
215+
private static void addHttpSessionListener( SessionHandler sessionHandler) {
216+
// Development
217+
HttpSessionListener httpSessionListener = new HttpSessionListener() {
218+
@Override
219+
public void sessionCreated(HttpSessionEvent se) {
220+
Log.info(Fuseki.serverLog, "Jakarta session created");
221+
}
222+
@Override
223+
public void sessionDestroyed(HttpSessionEvent se) {
224+
Log.info(Fuseki.serverLog, "Jakarta session destroyed");
225+
}
226+
};
227+
sessionHandler.addEventListener(httpSessionListener);
228+
}
226229
}

jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FusekiShiro.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939

4040
static void shiroEnvironment(ServletContext servletContext, List<String> possibleShiroIniFiles) {
4141
// Shiro environment initialization, done here because we don't have webapp listeners.
42-
EnvironmentLoaderListener shiroListener = new ShiroEnvironmentLoaderListener(possibleShiroIniFiles);
42+
EnvironmentLoaderListener shiroListener = new FusekiShiroEnvironmentLoaderListener(possibleShiroIniFiles);
4343
// Silence these - handle the errors ourselves.
4444
LogCtl.disable(org.apache.shiro.web.env.IniWebEnvironment.class);
4545
LogCtl.disable(org.apache.shiro.web.env.EnvironmentLoader.class);
@@ -83,13 +83,19 @@ static String huntForShiroIni(List<String> locations) {
8383
return null;
8484
}
8585

86+
/**
87+
* Ensure a filename is formatted the way SHiro wants it.
88+
*/
8689
public static String withResourcePrefix(String shiroFileName) {
8790
if ( shiroFileName.startsWith(fileShiroPrefix) )
8891
return shiroFileName;
8992
// How Shiro likes it. file:, unencoded filename.
9093
return fileShiroPrefix+shiroFileName;
9194
}
9295

96+
/**
97+
* Reverse formatting a file mae. See {@link #withResourcePrefix}.
98+
*/
9399
public static String removeResourcePrefix(String shiroFileResource) {
94100
if ( shiroFileResource.startsWith(fileShiroPrefix) ) {
95101
// Shiro format resource name.

jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/ShiroEnvironmentLoaderListener.java renamed to jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FusekiShiroEnvironmentLoaderListener.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@
3434
* locations for a {@code shiro.ini} file. It will return the first found in a list
3535
* of possible file names.
3636
*/
37-
class ShiroEnvironmentLoaderListener extends EnvironmentLoaderListener{
37+
class FusekiShiroEnvironmentLoaderListener extends EnvironmentLoaderListener{
3838

3939
private List<String> locations;
4040

41-
/*package*/ ShiroEnvironmentLoaderListener(List<String> locations) {
41+
/*package*/ FusekiShiroEnvironmentLoaderListener(List<String> locations) {
4242
this.locations = locations;
4343
}
4444

@@ -51,8 +51,10 @@ class ShiroEnvironmentLoaderListener extends EnvironmentLoaderListener{
5151
*/
5252
@Override
5353
protected void customizeEnvironment(WebEnvironment environment) {
54-
if ( locations == null )
54+
if ( locations == null ) {
55+
super.customizeEnvironment(environment);
5556
return;
57+
}
5658

5759
// Look for shiro.ini
5860
if ( environment instanceof ResourceBasedWebEnvironment ) {
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*
19+
* SPDX-License-Identifier: Apache-2.0
20+
*/
21+
22+
package org.apache.jena.fuseki.mod.shiro;
23+
24+
import java.util.List;
25+
26+
import jakarta.servlet.FilterChain;
27+
import jakarta.servlet.ServletException;
28+
import jakarta.servlet.ServletRequest;
29+
import jakarta.servlet.ServletResponse;
30+
import org.apache.jena.fuseki.FusekiException;
31+
import org.apache.shiro.subject.support.DefaultSubjectContext;
32+
import org.apache.shiro.web.servlet.ShiroFilter;
33+
34+
/**
35+
* FusekiShiroFilter, includes Shiro initialization. Fuseki is a
36+
* not a webapp so it needs to trigger off servlet initialization.
37+
*/
38+
/*package*/ class FusekiShiroFilter extends ShiroFilter {
39+
40+
private final String shiroInitializationResource;
41+
private final boolean disableSessions;
42+
43+
/*package*/ FusekiShiroFilter(String shiroResourceName, boolean disableSessions) {
44+
// Shiro file -- URLs are "file:<no encoding>"
45+
this.shiroInitializationResource = shiroResourceName;
46+
this.disableSessions = disableSessions;
47+
}
48+
49+
@Override
50+
public void init() throws Exception {
51+
try {
52+
// Intercept Shiro initialization.
53+
List<String> locations = List.of();
54+
if ( shiroInitializationResource != null ) {
55+
locations = List.of(shiroInitializationResource);
56+
}
57+
FusekiShiro.shiroEnvironment(getServletContext(), locations);
58+
} catch (FusekiException ex) {
59+
// Wrap so ShiroFilter does not log it.
60+
throw new ServletException(ex);
61+
}
62+
super.init();
63+
}
64+
65+
@Override
66+
protected ServletRequest prepareServletRequest(ServletRequest request, ServletResponse response, FilterChain chain) {
67+
if ( disableSessions ) {
68+
// GH-4033 : https://github.com/apache/jena/issues/4033
69+
// See NoSessionCreationFilter
70+
request.setAttribute(DefaultSubjectContext.SESSION_CREATION_ENABLED, Boolean.FALSE);
71+
}
72+
return super.prepareServletRequest(request, response, chain);
73+
}
74+
}

0 commit comments

Comments
 (0)