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

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

commit b4fb486a2ac815b2db18f136ccf4b6fbb1efff87
Author: Andy Seaborne <[email protected]>
AuthorDate: Tue Dec 24 08:39:38 2024 +0000

    GH-2902: Adjust command line args via customizer
---
 .../java/org/apache/jena/atlas/lib/FileOps.java    |   3 +-
 .../main/java/org/apache/jena/fuseki/Fuseki.java   |   3 +
 .../apache/jena/fuseki/server/FusekiCoreInfo.java  |  10 +-
 .../apache/jena/fuseki/server/PlatformInfo.java    |  18 +--
 .../apache/jena/fuseki/system/FusekiLogging.java   |   7 +-
 jena-fuseki2/jena-fuseki-main/pom.xml              |  16 +++
 .../apache/jena/fuseki/main/FusekiMainInfo.java    |  61 ----------
 .../apache/jena/fuseki/main/cmds/FusekiMain.java   |  30 +++--
 .../jena/fuseki/main/cmds/FusekiServerCmd.java     |   4 +-
 .../apache/jena/fuseki/main/cmds/ServerArgs.java   |   4 +-
 .../org/apache/jena/fuseki/mgt/ActionDatasets.java |  13 ++-
 .../apache/jena/fuseki/mgt/FusekiServerCtl.java    | 129 ++++++++++-----------
 .../java/org/apache/jena/fuseki/mgt/Template.java  |   7 --
 .../apache/jena/fuseki/mgt/TemplateFunctions.java  |  15 ++-
 .../apache/jena/fuseki/mod/FusekiModServer.java    |  91 ---------------
 .../apache/jena/fuseki/mod/FusekiServerRunner.java | 114 ++++++++++++++++++
 .../jena/fuseki/mod/admin/ArgModuleAdmin.java      |  69 -----------
 .../apache/jena/fuseki/mod/admin/FMod_Admin.java   |  58 +++++----
 .../fuseki/mod/prometheus/FMod_Prometheus.java     |   9 +-
 .../apache/jena/fuseki/mod/shiro/FMod_Shiro.java   |  18 ++-
 .../jena/fuseki/mod/shiro/FusekiShiroLib.java      |   5 +-
 .../mod/shiro/ShiroEnvironmentLoaderListener.java  |   4 +-
 .../org/apache/jena/fuseki/mod/ui/FMod_UI.java     |  44 ++++---
 .../org/apache/jena/fuseki/mod/TS_FusekiMods.java  |   3 +-
 .../apache/jena/fuseki/mod/TestFusekiServer.java   |  68 ++++++++++-
 .../apache/jena/fuseki/mod/admin/TestAdmin.java    |  20 ++--
 .../fuseki/mod/admin/TestTemplateAddDataset.java   |   2 +-
 .../jena/fuseki/mod/metrics/TestModPrometheus.java |   2 +-
 .../apache/jena/fuseki/mod/shiro/TestModShiro.java |  12 +-
 jena-fuseki2/pom.xml                               |   3 +-
 30 files changed, 414 insertions(+), 428 deletions(-)

diff --git a/jena-base/src/main/java/org/apache/jena/atlas/lib/FileOps.java 
b/jena-base/src/main/java/org/apache/jena/atlas/lib/FileOps.java
index 15d84eb7dd..0e77118502 100644
--- a/jena-base/src/main/java/org/apache/jena/atlas/lib/FileOps.java
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/FileOps.java
@@ -65,6 +65,7 @@ public class FileOps {
 
     /**
      * Delete all files in a directory.
+     * Does not recurse in the direction.
      * Does nothing if the path name does not exist or is not a directory.
      *
      * @param dir
@@ -84,7 +85,7 @@ public class FileOps {
         clearAll(new File(d)) ;
     }
 
-    /** Delete all files and directories (recursively) in a directory */
+    /** Delete all files and directories (recursively) in a directory; does 
not delete the directory argument. */
     public static void clearAll(File d) {
         if ( ! d.exists() )
             return ;
diff --git 
a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/Fuseki.java
 
b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/Fuseki.java
index a8c4a0680c..2c39ab0497 100644
--- 
a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/Fuseki.java
+++ 
b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/Fuseki.java
@@ -182,7 +182,10 @@ public class Fuseki {
     public static final String attrNameRegistry            = 
"org.apache.jena.fuseki:DataAccessPointRegistry";
     public static final String attrOperationRegistry       = 
"org.apache.jena.fuseki:OperationRegistry";
     public static final String attrAuthorizationService    = 
"org.apache.jena.fuseki:AuthorizationService";
+    // The Fuseki Server
     public static final String attrFusekiServer            = 
"org.apache.jena.fuseki:Server";
+    // The FusekiServerCtl object for the admin area; may be null
+    public static final String attrFusekiServerCtl         = 
"org.apache.jena.fuseki:ServerCtl";
     public static final String attrMetricsProvider         = 
"org.apache.jena.fuseki:MetricsProvider";
 
     public static void setVerbose(ServletContext cxt, boolean verbose) {
diff --git 
a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiCoreInfo.java
 
b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiCoreInfo.java
index 0b64600f25..e143ec9d8d 100644
--- 
a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiCoreInfo.java
+++ 
b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiCoreInfo.java
@@ -37,7 +37,6 @@ public class FusekiCoreInfo {
     }
 
     /** Log details - this function is about command line details */
-    // Shared between FusekiMain and Fuseki Webapp (currently).
     public static void logServerCmdSetup(Logger log, boolean verbose, 
DataAccessPointRegistry dapRegistry,
                                          String datasetPath, String 
datasetDescription, String serverConfigFile, String staticFiles) {
         if ( datasetPath != null )
@@ -46,16 +45,11 @@ public class FusekiCoreInfo {
             FmtLog.info(log, "Configuration file: %s", serverConfigFile);
 
         FusekiCoreInfo.logDataAccessPointRegistry(log, dapRegistry, verbose);
-
         if ( staticFiles != null )
             FmtLog.info(log, "Static files: %s", staticFiles);
-
-        if ( verbose ) {
-            PlatformInfo.logDetailsSystem(log);
+        PlatformInfo.logDetailsSystem(log);
+        if ( verbose )
             PlatformInfo.logDetailsJVM(log);
-        }
-        else
-            PlatformInfo.logDetailsSystemPlain(log);
     }
 
     /** Log a {@link DataAccessPointRegistry} */
diff --git 
a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/PlatformInfo.java
 
b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/PlatformInfo.java
index 7cd171ac33..98414a1668 100644
--- 
a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/PlatformInfo.java
+++ 
b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/PlatformInfo.java
@@ -24,7 +24,8 @@ import java.util.function.Function;
 import org.apache.jena.atlas.logging.FmtLog;
 import org.slf4j.Logger;
 
-public class PlatformInfo {
+/** Platform inforamtion - OS and JVM */
+/*package*/ class PlatformInfo {
 
     public static void main(String ...args) throws IOException {
         long maxMem = Runtime.getRuntime().maxMemory();
@@ -35,14 +36,8 @@ public class PlatformInfo {
         System.out.printf("max=%s  total=%s  used=%s  free=%s\n", 
f.apply(maxMem), f.apply(totalMem), f.apply(usedMem), f.apply(freeMem));
     }
 
-    /** System details section */
-    public static void logDetailsSystem(Logger log) {
-        log.info("System");
-        logDetailsSystemPlain(log);
-    }
-
-    /** System details, no section header */
-    public static void logDetailsSystemPlain(Logger log) {
+    /** System details */
+    /*package*/ static void logDetailsSystem(Logger log) {
         String prefix = "  ";
         long maxMem = Runtime.getRuntime().maxMemory();
         long totalMem = Runtime.getRuntime().totalMemory();
@@ -60,19 +55,16 @@ public class PlatformInfo {
     }
 
     /** JVM details section. */
-    public static void logDetailsJVM(Logger log) {
+    /*package*/ static void logDetailsJVM(Logger log) {
         String prefix = "  ";
-        log.info("Java");
         logOne(log, prefix, "java.vendor");
         logOne(log, prefix, "java.home");
         logOne(log, prefix, "java.runtime.version");
         logOne(log, prefix, "java.runtime.name");
-        //logOne(log, "java.endorsed.dirs");
         logOne(log, prefix, "user.language");
         logOne(log, prefix, "user.timezone");
         logOne(log, prefix, "user.country");
         logOne(log, prefix, "user.dir");
-        //logOne(log, prefix, "file.encoding");
     }
 
     private static void logOne(Logger log, String prefix, String property) {
diff --git 
a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/system/FusekiLogging.java
 
b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/system/FusekiLogging.java
index a6356b057e..69f85a957e 100644
--- 
a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/system/FusekiLogging.java
+++ 
b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/system/FusekiLogging.java
@@ -232,8 +232,7 @@ public class FusekiLogging
     }
 
     private static String log4j2setupFallback() {
-        // The logging file for Fuseki in Tomcat webapp is in 
"log4j2.properties" in the webapp root directory.
-        // This is used by command line Fuseki (full and main)
+        // This is the default
 
 //      filters = threshold
 //      filter.threshold.type = ThresholdFilter
@@ -247,6 +246,7 @@ public class FusekiLogging
                 appender.console.name = OUT
                 appender.console.target = SYSTEM_OUT
                 appender.console.layout.type = PatternLayout
+                ##appender.console.layout.pattern = %d{HH:mm:ss} %-5p %-15c{1} 
:: %m%n
                 appender.console.layout.pattern = [%d{yyyy-MM-dd HH:mm:ss}] 
%-10c{1} %-5p %m%n
 
                 rootLogger.level                  = WARN
@@ -297,8 +297,7 @@ public class FusekiLogging
                 logger.fuseki-request.additivity             = false
                 logger.fuseki-request.level                  = OFF
                 logger.fuseki-request.appenderRef.plain.ref  = PLAIN
-                    );
-                    """;
+               """;
     }
 
     public static void resetLogging(String configString) {
diff --git a/jena-fuseki2/jena-fuseki-main/pom.xml 
b/jena-fuseki2/jena-fuseki-main/pom.xml
index b72e861327..97afefa87c 100644
--- a/jena-fuseki2/jena-fuseki-main/pom.xml
+++ b/jena-fuseki2/jena-fuseki-main/pom.xml
@@ -59,8 +59,24 @@
     <dependency>
       <groupId>org.apache.jena</groupId>
       <artifactId>jena-cmds</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <!-- Add for runtime in jena-fuseki-server -->
+    <dependency>
+      <groupId>org.apache.jena</groupId>
+      <artifactId>jena-fuseki-ui</artifactId>
       <version>5.3.0-SNAPSHOT</version>
+      <scope>test</scope>
+    </dependency>
+
+    <!-- Add in jena-fuseki-server
+    <dependency>
+      <groupId>org.apache.jena</groupId>
+      <artifactId>jena-text</artifactId>
+      <version>${project.version}</version>
     </dependency>
+    -->
 
     <dependency>
       <groupId>org.eclipse.jetty.ee10</groupId>
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiMainInfo.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiMainInfo.java
deleted file mode 100644
index bcdf758ddb..0000000000
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiMainInfo.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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
- *
- *     http://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.
- */
-
-package org.apache.jena.fuseki.main;
-
-import org.apache.jena.atlas.logging.FmtLog;
-import org.apache.jena.fuseki.server.FusekiCoreInfo;
-import org.slf4j.Logger;
-
-public class FusekiMainInfo {
-
-    /** Details of the code version. */
-    public static void logCode(Logger log) {
-        FusekiCoreInfo.logCode(log);
-    }
-
-    /** Log server details. */
-    public static void logServer(Logger log, FusekiServer server, boolean 
verbose) {
-        FusekiMainInfo.logServerConnections(log, server);
-        FusekiMainInfo.logServerDatasets(log, server, verbose);
-        if ( server.getStaticContentDir() != null )
-            FmtLog.info(log,  "Static files: %s", 
server.getStaticContentDir());
-    }
-
-    /** Log details about the code version */
-    public static void logServerCode(Logger log) {
-        FusekiCoreInfo.logCode(log);
-    }
-
-    /** The the server connection setup */
-    public static void logServerConnections(Logger log, FusekiServer server) {
-        int httpsPort = server.getHttpsPort();
-        int httpPort = server.getHttpPort();
-        if ( httpsPort > 0 && httpPort > 0 )
-            log.info("Ports: http="+httpPort+" https="+httpsPort);
-        else if ( httpsPort <= 0 )
-            log.info("Port: http="+httpPort);
-        else if ( httpPort <= 0 )
-            log.info("Port: https="+httpsPort);
-    }
-
-    /** Log information about datasets in this server */
-    public static void logServerDatasets(Logger log, FusekiServer server, 
boolean longForm) {
-        FusekiCoreInfo.logDataAccessPointRegistry(log, 
server.getDataAccessPointRegistry(), longForm);
-    }
-}
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/FusekiMain.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/FusekiMain.java
index d902c9ce10..6f4636b730 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/FusekiMain.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/FusekiMain.java
@@ -36,7 +36,6 @@ import org.apache.jena.atlas.web.AuthScheme;
 import org.apache.jena.cmd.*;
 import org.apache.jena.fuseki.Fuseki;
 import org.apache.jena.fuseki.FusekiException;
-import org.apache.jena.fuseki.main.FusekiMainInfo;
 import org.apache.jena.fuseki.main.FusekiServer;
 import org.apache.jena.fuseki.main.sys.FusekiModules;
 import org.apache.jena.fuseki.main.sys.FusekiServerArgsCustomiser;
@@ -56,6 +55,7 @@ import org.apache.jena.sys.JenaSystem;
 import org.slf4j.Logger;
 
 public class FusekiMain extends CmdARQ {
+
     /** Default HTTP port when running from the command line. */
     public static int defaultPort          = 3030;
     /** Default HTTPS port when running from the command line. */
@@ -97,8 +97,8 @@ public class FusekiMain extends CmdARQ {
     private static ArgDecl  argWithMetrics  = new ArgDecl(ArgDecl.NoValue,  
"withMetrics", "metrics");
     private static ArgDecl  argWithCompact  = new ArgDecl(ArgDecl.NoValue,  
"withCompact", "compact");
 
-//    // Use modules found by the ServiceLoader.
-//    private static ArgDecl  argEnableModules  = new 
ArgDecl(ArgDecl.HasValue,  "modules", "fuseki-modules");
+    // Use modules found by the ServiceLoader. Currently, no-op.
+    private static ArgDecl  argEnableModules  = new ArgDecl(ArgDecl.HasValue,  
"modules", "fuseki-modules");
 
     private static ArgDecl  argAuth         = new ArgDecl(ArgDecl.HasValue, 
"auth");
 
@@ -196,6 +196,20 @@ public class FusekiMain extends CmdARQ {
         ArgCustomizers.addCustomiser(customiser);
     }
 
+    /**
+     * Registers CLI customisers.
+     * <p>
+     * CLI customisers can add one/more custom arguments into the Fuseki 
Server CLI arguments and then can apply those
+     * to the Fuseki server being built during the processing of {@link 
#processModulesAndArgs()}.  This allows for
+     * custom arguments that directly affect how the Fuseki server is built to 
be created.
+     * </p>
+     * @see #addCustomiser(FusekiServerArgsCustomiser)
+     */
+    public static void addCustomisers(FusekiModules customiserSet) {
+        Objects.requireNonNull(customiserSet);
+        
customiserSet.forEach(customiser->ArgCustomizers.addCustomiser(customiser));
+    }
+
     /**
      * Resets any previously registered CLI customisers
      */
@@ -306,7 +320,7 @@ public class FusekiMain extends CmdARQ {
         add(argWithMetrics, "--metrics",    "Enable /$/metrics");
         add(argWithCompact, "--compact",    "Enable /$/compact/*");
 
-        //add(argEnableModules, "--modules=true|false", "Enable Fuseki 
modules");
+        add(argEnableModules, "--modules=true|false", "Enable Fuseki 
autoloaded modules");
 
         super.modVersion.addClass("Fuseki", Fuseki.class);
 
@@ -578,9 +592,9 @@ public class FusekiMain extends CmdARQ {
         // Allows for external setting of serverArgs.fusekiModules
         if ( serverArgs.fusekiModules == null ) {
             // Get modules from system-wide setup.
-            // This (Fuseki 5.3.0- defaults to an empty set of modules.
-//            boolean withModules = hasValueOfTrue(argEnableModules);
-            serverArgs.fusekiModules = FusekiModules.getSystemModules();
+            boolean withModules = hasValueOfTrue(argEnableModules);
+            if ( withModules )
+                serverArgs.fusekiModules = FusekiModules.getSystemModules();
         }
 
         if ( contains(argCORS) ) {
@@ -630,7 +644,7 @@ public class FusekiMain extends CmdARQ {
         // Check for command line or config setup.
         try {
             Logger log = Fuseki.serverLog;
-            FusekiMainInfo.logServerCode(log);
+            FusekiCoreInfo.logCode(log);
             FusekiServer server = makeServer(serverArgs);
             infoCmd(server, log);
             try {
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/FusekiServerCmd.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/FusekiServerCmd.java
index 72c121a1c3..1f4dc87fe7 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/FusekiServerCmd.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/FusekiServerCmd.java
@@ -18,7 +18,7 @@
 
 package org.apache.jena.fuseki.main.cmds;
 
-import org.apache.jena.fuseki.mod.FusekiModServer;
+import org.apache.jena.fuseki.mod.FusekiServerRunner;
 import org.apache.jena.fuseki.system.FusekiLogging;
 
 /** Fuseki command that runs a Fuseki server with the admin UI.
@@ -45,7 +45,7 @@ public class FusekiServerCmd {
      * syntax but not start it.
      */
     static public void main(String... args) {
-        FusekiModServer.runAsync(args).join();
+        FusekiServerRunner.main(args);
     }
 }
 
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/ServerArgs.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/ServerArgs.java
index d627adddc9..cad50c97f3 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/ServerArgs.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/ServerArgs.java
@@ -52,7 +52,7 @@ public class ServerArgs {
     public boolean verboseLogging         = false;
 
     /**
-     * FusekiModules to use during the server build *
+     * FusekiModules to use during the server build
      * Command line customisers are handled separately by FusekiMain.
      */
     public FusekiModules fusekiModules    = null;
@@ -75,7 +75,7 @@ public class ServerArgs {
     public String serverConfigFile        = null;
     public Model serverConfigModel        = null;
 
-    /** Allow no datasets without it being an error. This is not an argument. 
*/
+    /** Allow no datasets without it being an error. This is not a command 
argument. */
     public boolean allowEmpty             = false;
     public SetupType setup                = SetupType.UNSET;
     /** Start without a dataset or configuration (this is {@code --empty}) */
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java
index 27cfd88836..3b30d82b7f 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java
@@ -211,7 +211,7 @@ public class ActionDatasets extends ActionContainerItem {
                     FmtLog.warn(action.log, "Inconsistent names: datasetPath = 
%s; DataAccessPoint name = %s", datasetPath, dataAccessPoint);
                 succeeded = true;
                 action.getDataAccessPointRegistry().register(dataAccessPoint);
-                
+
                 // Add to metrics
                 MetricsProvider metricProvider = action.getMetricsProvider();
                 if ( metricProvider != null )
@@ -418,7 +418,14 @@ public class ActionDatasets extends ActionContainerItem {
             params.put(Template.NAME, dbName.substring(1));
         else
             params.put(Template.NAME, dbName);
-        FusekiServerCtl.addGlobals(params);
+
+        FusekiServerCtl serverCtl = 
FusekiServerCtl.get(action.getServletContext());
+        if ( serverCtl != null )
+            serverCtl.addGlobals(params);
+        else {
+            ServletOps.errorOccurred("No admin area");
+            // No return.
+        }
 
         //action.log.info(format("[%d] Create database : name = %s, type = 
%s", action.id, dbName, dbType ));
 
@@ -429,7 +436,7 @@ public class ActionDatasets extends ActionContainerItem {
             ServletOps.errorBadRequest(format("dbType can be only one of %s", 
keys));
         }
 
-        String instance = TemplateFunctions.templateFile(template, params, 
Lang.TTL);
+        String instance = 
TemplateFunctions.templateFile(serverCtl.getFusekiBase(), template, params, 
Lang.TTL);
         RDFParser.create().source(new 
StringReader(instance)).base("http://base/";).lang(Lang.TTL).parse(dest);
     }
 
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mgt/FusekiServerCtl.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mgt/FusekiServerCtl.java
index 3710157122..0407df4e04 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mgt/FusekiServerCtl.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mgt/FusekiServerCtl.java
@@ -29,18 +29,21 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.*;
 
+import jakarta.servlet.ServletContext;
 import org.apache.jena.atlas.RuntimeIOException;
 import org.apache.jena.atlas.io.IOX;
 import org.apache.jena.atlas.lib.FileOps;
 import org.apache.jena.atlas.lib.InternalErrorException;
 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.CmdException;
 import org.apache.jena.dboe.sys.Names;
 import org.apache.jena.fuseki.Fuseki;
 import org.apache.jena.fuseki.FusekiConfigException;
 import org.apache.jena.fuseki.build.DatasetDescriptionMap;
 import org.apache.jena.fuseki.build.FusekiConfig;
+import org.apache.jena.fuseki.main.cmds.FusekiMain;
 import org.apache.jena.fuseki.server.DataAccessPoint;
 import org.apache.jena.fuseki.server.DataService;
 import org.apache.jena.fuseki.server.FusekiVocabG;
@@ -57,20 +60,23 @@ import org.apache.jena.sparql.core.assembler.AssemblerUtils;
 import org.apache.jena.system.G;
 
 public class FusekiServerCtl {
-    /**
-     * Root of the varying files in this deployment. Often $PWD/run.
-     * This location must be writable.
-     */
-    public static Path FUSEKI_BASE = null;
-
-    public static final String DFT_SHIRO_INI          = "shiro.ini";
     public static final String envFusekiBase          = "FUSEKI_BASE";
     public static final String envFusekiShiro         = "FUSEKI_SHIRO";
+    public static final String DFT_SHIRO_INI          = "shiro.ini";
+
+    public static FusekiServerCtl get(ServletContext cxt) {
+        if ( cxt == null )
+           return null;
+        FusekiServerCtl fusekiServerCtl = 
(FusekiServerCtl)cxt.getAttribute(Fuseki.attrFusekiServerCtl);
+        if ( fusekiServerCtl == null )
+            Log.warn(FusekiServerCtl.class, "No FusekiServerCtl in 
ServletContext");
+        return fusekiServerCtl;
+    }
 
-    // Relative names of directories in the FUSEKI_BASE area.
+    // Relative names of directories in the Fuseki base area.
     private static final String databasesLocationBase = "databases";
     // Place to put Lucene text and spatial indexes.
-    private static final String    databaseIndexesDir = "text_indexes";
+    private static final String databaseIndexesDir    = "text_indexes";
 
     private static final String backupDirNameBase     = "backups";
     private static final String configDirNameBase     = "configuration";
@@ -84,7 +90,6 @@ public class FusekiServerCtl {
     public static int levelFModUI                     = 
BaseFusekiAutoModuleLevel+10;
     public static int levelFModShiro                  = 
BaseFusekiAutoModuleLevel+20;
 
-
     /** Directory for TDB databases - this is known to the assembler templates 
*/
     public static Path        dirDatabases       = null;
 
@@ -107,73 +112,53 @@ public class FusekiServerCtl {
     // Marks the end of successful initialization.
     /*package*/static boolean serverInitialized  = false;
 
-//    /** OLD
-//     * Root of the Fuseki installation for fixed files.
-//     * This may be null (e.g. running inside a web application container)
-//     */
-//    public static Path FUSEKI_HOME = null;
-
-    // Too much is done with statics, assuming one server+admin process
-    // At the moment, it is one setup happening at a time.
-    // Once created, it is independent.
-    // Better, less statics, more FusekiServerApp instance object.
-    // Current limitation: FUSEKI_BASE is static and set in
-    //    FusekiApp.java          line 275
-    //    ArgModuleAdmin.java     line 61
-    //    FMod_Admin.java         line 117
-    //    FMod_UI.java            line 133
-    //    Template.java           line 27
-    //    ActionDadasets uses addGlobals.
-
     // Default - "run" in the current directory.
     public static final String dftFusekiBase = "run";
 
-    public FusekiServerCtl(String location) {
-        if ( location == null ) {
-            FUSEKI_BASE = null;
-            return;
-        }
-        FUSEKI_BASE = Path.of(location);
-    }
+    private Path fusekiBase = null;
 
-    public Path setup() {
-        // Set the location of the BASE area
-        setFusekiBase();
-        // Ensure the BASE area exists on disk.
-        setBaseAreaOnDisk();
-        // Format the BASE area.
-        ensureBaseArea(FUSEKI_BASE);
-        return FUSEKI_BASE;
+
+    public FusekiServerCtl(Path location) {
+        if ( location == null )
+            location = envFusekiBase();
+
+        this.fusekiBase = location;
     }
 
-    private void setFusekiBase() {
-        if ( FUSEKI_BASE == null )
-            FUSEKI_BASE = select_FUSEKI_BASE();
+    public Path getFusekiBase() {
+        return fusekiBase;
     }
 
-    private Path select_FUSEKI_BASE() {
+
+    private Path envFusekiBase() {
         // Does not guarantee existence
-        Path setting = null;
-        if ( FUSEKI_BASE == null )
-            setting = calc_FUSEKI_BASE();
+        if ( fusekiBase != null )
+            return fusekiBase;
+        String valueFusekiBase = getenv("FUSEKI_BASE");
+        if ( valueFusekiBase == null )
+            valueFusekiBase = dftFusekiBase;
+        Path setting = Path.of(valueFusekiBase);
         setting = setting.toAbsolutePath();
         return setting;
     }
 
-    private Path calc_FUSEKI_BASE() {
-        String valueFusekiBase = getenv("FUSEKI_BASE");
-        if ( valueFusekiBase == null )
-            valueFusekiBase = dftFusekiBase;
-        return Path.of(valueFusekiBase);
+    /**
+     *  Set up the area if not already formatted.
+     */
+    public void setup() {
+        // Ensure the BASE area exists on disk.
+        setBaseAreaOnDisk();
+        // Format the BASE area.
+        ensureBaseArea();
     }
 
     private void setBaseAreaOnDisk() {
-        FmtLog.info(Fuseki.configLog, "FUSEKI_BASE=%s", FUSEKI_BASE);
-        if ( ! Files.exists(FUSEKI_BASE) ) {
+        FmtLog.info(Fuseki.configLog, "Fuseki Base = %s", fusekiBase);
+        if ( ! Files.exists(fusekiBase) ) {
             try {
-                Files.createDirectories(FUSEKI_BASE);
+                Files.createDirectories(fusekiBase);
             } catch (IOException e) {
-                throw new FusekiConfigException("Failed to create FUSEKI_BASE: 
"+FUSEKI_BASE);
+                throw new FusekiConfigException("Failed to create Fuseki Base: 
"+fusekiBase);
             }
         }
         // Further checks in ensureBaseArea
@@ -182,17 +167,18 @@ public class FusekiServerCtl {
     /**
      * Create directories if found to be missing.
      */
-    private void ensureBaseArea(Path baseArea) {
+    private void ensureBaseArea() {
+        Path baseArea = fusekiBase;
         if ( Files.exists(baseArea) ) {
             if ( ! Files.isDirectory(baseArea) )
-                throw new FusekiConfigException("FUSEKI_BASE is not a 
directory: "+baseArea);
+                throw new FusekiConfigException("Fuseki base is not a 
directory: "+baseArea);
             if ( ! Files.isWritable(baseArea) )
-                throw new FusekiConfigException("FUSEKI_BASE is not writable: 
"+baseArea);
+                throw new FusekiConfigException("Fuseki base is not writable: 
"+baseArea);
         } else {
             ensureDir(baseArea);
         }
 
-        // Ensure FUSEKI_BASE has the assumed directories.
+        // Ensure the Fuseki base area has the assumed directories.
         dirTemplates        = writeableDirectory(baseArea, templatesNameBase);
         dirDatabases        = writeableDirectory(baseArea, 
databasesLocationBase);
         dirBackups          = writeableDirectory(baseArea, backupDirNameBase);
@@ -202,7 +188,7 @@ public class FusekiServerCtl {
 
         // ---- Initialize with files.
 
-//        // Copy missing files into FUSEKI_BASE
+        // Copy missing files into the Fuseki base
         // Interacts with FMod_Shiro.
         if ( Lib.getenv(FusekiServerCtl.envFusekiShiro) == null ) {
             copyFileIfMissing(null, DFT_SHIRO_INI, baseArea);
@@ -256,7 +242,7 @@ public class FusekiServerCtl {
         // DRY -- ActionDatasets (and others?)
         addGlobals(params);
 
-        String str = TemplateFunctions.templateFile(templateFile, params, 
Lang.TTL);
+        String str = TemplateFunctions.templateFile(fusekiBase, templateFile, 
params, Lang.TTL);
         Lang lang = RDFLanguages.filenameToLang(str, Lang.TTL);
 
         Graph configuration = RDFParser.fromString(str, lang).toGraph();
@@ -271,16 +257,14 @@ public class FusekiServerCtl {
         return dap;
     }
 
-    public static void addGlobals(Map<String, String> params) {
+    public void addGlobals(Map<String, String> params) {
         if ( params == null ) {
             Fuseki.configLog.warn("FusekiApp.addGlobals : params is null", new 
Throwable());
             return;
         }
 
         if ( ! params.containsKey("FUSEKI_BASE") )
-            params.put("FUSEKI_BASE", pathStringOrElse(FUSEKI_BASE, "unset"));
-//        if ( ! params.containsKey("FUSEKI_HOME") )
-//            params.put("FUSEKI_HOME", 
pathStringOrElse(FusekiAppEnv.FUSEKI_HOME, "unset"));
+            params.put("FUSEKI_BASE", pathStringOrElse(fusekiBase, "unset"));
     }
 
     /** Copy a file from src to dst under name fn.
@@ -391,6 +375,13 @@ public class FusekiServerCtl {
         return p;
     }
 
+    /** Running a full-features server sets some global state. Clear this up. 
(mainly for tests.)*/
+    public static void clearUpSystemState() {
+        System.getProperties().remove(FusekiServerCtl.envFusekiShiro);
+        System.getProperties().remove(FusekiServerCtl.envFusekiBase);
+        FusekiMain.resetCustomisers();
+    }
+
     private static Path makePath(Path root , String relName ) {
         Path path = root.resolve(relName);
         // Must exist
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mgt/Template.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mgt/Template.java
index 1266fb3faf..04007dec8c 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mgt/Template.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mgt/Template.java
@@ -18,15 +18,8 @@
 
 package org.apache.jena.fuseki.mgt;
 
-import java.nio.file.Path;
-
-
 public class Template
 {
-    public static Path getPath(String templateName) {
-        return FusekiServerCtl.FUSEKI_BASE.resolve(templateName);
-    }
-
     public static final String templateDir            = "templates";
 
     // These are used by the command line start up.
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mgt/TemplateFunctions.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mgt/TemplateFunctions.java
index 009cc655ae..a6b48c22c0 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mgt/TemplateFunctions.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mgt/TemplateFunctions.java
@@ -20,6 +20,7 @@ package org.apache.jena.fuseki.mgt;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.file.Path;
 import java.util.Map;
 import java.util.Map.Entry;
 
@@ -31,13 +32,15 @@ import org.apache.jena.util.FileUtils;
 public class TemplateFunctions
 {
     /** Read in a template from a file, substitute for {NAME} and return the 
string. */
-    public static String templateFile(String templateName, Map<String, String> 
params, Lang lang) {
-        String templateFilename = Template.getPath(templateName).toString();
+    public static String templateFile(Path directory, String templateName, 
Map<String, String> params, Lang lang) {
+        String templateFilename = directory.resolve(templateName).toString();
         String template;
-        try { template = FileUtils.readWholeFileAsUTF8(templateFilename); }
-        catch (IOException ex) {
+        try {
+            template = FileUtils.readWholeFileAsUTF8(templateFilename);
+        } catch (IOException ex) {
             Fuseki.serverLog.error("File not found: "+templateFilename);
-            IO.exception(ex); return null;
+            IO.exception(ex);
+            return null;
         }
         return templateString(template, params, lang);
     }
@@ -74,7 +77,7 @@ public class TemplateFunctions
                      Lang.JSONLD.equals(lang)  ||
                      Lang.RDFJSON.equals(lang)
                     ) {
-                    // Make safe for a RDF language ""-string - especially MS 
Windows \ path separators.
+                    // Make safe for an RDF language ""-string - especially MS 
Windows \ path separators.
                     x = x.replace("\\", "\\\\");
                     x = x.replace("\"", "\\\"");
                 }
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/FusekiModServer.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/FusekiModServer.java
deleted file mode 100644
index 84f0fb72f6..0000000000
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/FusekiModServer.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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
- *
- *     http://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.
- */
-
-package org.apache.jena.fuseki.mod;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import org.apache.jena.atlas.lib.FileOps;
-import org.apache.jena.fuseki.main.FusekiServer;
-import org.apache.jena.fuseki.main.cmds.FusekiMain;
-import org.apache.jena.fuseki.main.sys.FusekiModule;
-import org.apache.jena.fuseki.main.sys.FusekiModules;
-import org.apache.jena.fuseki.mod.admin.FMod_Admin;
-import org.apache.jena.fuseki.mod.prometheus.FMod_Prometheus;
-import org.apache.jena.fuseki.mod.shiro.FMod_Shiro;
-import org.apache.jena.fuseki.mod.ui.FMod_UI;
-
-public class FusekiModServer {
-
-    public static void main(String... args) {
-        runAsync(args).join();
-    }
-
-    public static FusekiServer runAsync(String... args) {
-        return construct(args).start();
-    }
-
-    public static FusekiServer construct(String... args) {
-        // Order: FMod_Admin before FMod_Shiro
-        // These modules may have state that is carried across the build steps.
-        FusekiModule fmodShiro = FMod_Shiro.create();
-        FusekiModule fmodAdmin = FMod_Admin.create();
-
-        FusekiModules serverModules = FusekiModules.create( fmodAdmin
-                                                          , FMod_UI.get()
-                                                          , fmodShiro
-                                                          , 
FMod_Prometheus.get() );
-        serverModules.forEach(FusekiMain::addCustomiser);
-
-        System.setProperty("FUSEKI_BASE", "run");
-        FileOps.ensureDir("run");
-
-        // Adjust args.
-        List<String> argList = Arrays.asList(args);
-        // Ensure "--empty", "--modules=true"
-        // Better?: moded startup - i.e. setting defaults.
-
-        if ( args.length == 0 ) {
-            String [] defaultArgs = { "--port=3030", "--empty" };
-            args = defaultArgs;
-        } else {
-            List<String> argsList = new ArrayList<String>(Arrays.asList(args));
-            if ( ! containsArg(argList, "--?empty") )
-                argsList.add(0, "--empty"); // addFirst in java21
-            if ( ! containsArg(argList, "--?modules") )
-                argsList.add(0, "--modules=true");
-            args = argsList.toArray(args);
-        }
-
-        FusekiModules modules = serverModules;
-        // Set system modules - these are picked up in FusekiMain
-        FusekiModules.setSystemDefault(modules);
-        FusekiServer server = FusekiServer.construct(args);
-        return server;
-    }
-
-    private static boolean containsArg(List<String> argList, String argRegex) {
-        //Pattern pattern = Pattern.compile(argRegex);
-
-        return argList.stream().anyMatch(arg->{
-            return arg.matches(argRegex);
-        });
-    }
-}
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/FusekiServerRunner.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/FusekiServerRunner.java
new file mode 100644
index 0000000000..e86532594b
--- /dev/null
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/FusekiServerRunner.java
@@ -0,0 +1,114 @@
+/*
+ * 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
+ *
+ *     http://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.
+ */
+
+package org.apache.jena.fuseki.mod;
+
+import java.net.BindException;
+
+import org.apache.jena.atlas.lib.FileOps;
+import org.apache.jena.atlas.lib.Lib;
+import org.apache.jena.cmd.CmdGeneral;
+import org.apache.jena.fuseki.Fuseki;
+import org.apache.jena.fuseki.FusekiException;
+import org.apache.jena.fuseki.main.FusekiServer;
+import org.apache.jena.fuseki.main.cmds.FusekiMain;
+import org.apache.jena.fuseki.main.cmds.ServerArgs;
+import org.apache.jena.fuseki.main.sys.FusekiModule;
+import org.apache.jena.fuseki.main.sys.FusekiModules;
+import org.apache.jena.fuseki.main.sys.FusekiServerArgsCustomiser;
+import org.apache.jena.fuseki.mgt.FusekiServerCtl;
+import org.apache.jena.fuseki.mod.admin.FMod_Admin;
+import org.apache.jena.fuseki.mod.prometheus.FMod_Prometheus;
+import org.apache.jena.fuseki.mod.shiro.FMod_Shiro;
+import org.apache.jena.fuseki.mod.ui.FMod_UI;
+
+public class FusekiServerRunner {
+
+    public static void main(String... args) {
+        //runAsync(args).join();
+        prepareFusekiMain();
+        FusekiMain.run(args);
+    }
+
+    public static FusekiServer runAsync(String... args) {
+        FusekiServer server = construct(args);
+        try {
+            server.start();
+        } catch (FusekiException ex) {
+            if ( ex.getCause() instanceof BindException ) {
+//                if ( serverArgs.jettyConfigFile == null )
+//                    Fuseki.serverLog.error("Failed to start server: 
"+ex.getCause().getMessage()+ ": port="+serverArgs.port);
+//                else
+//                    Fuseki.serverLog.error("Failed to start server: 
"+ex.getCause().getMessage()+ ": port in use");
+                Fuseki.serverLog.error("Failed to start server: 
"+ex.getCause().getMessage()+ ": port in use");
+                System.exit(1);
+            }
+            throw ex;
+        } catch (Exception ex) {
+            throw new FusekiException("Failed to start server: " + 
ex.getMessage(), ex);
+        }
+        return server.start();
+    }
+
+    public static FusekiServer construct(String... args) {
+        prepareFusekiMain();
+        // Make server
+        FusekiServer server = FusekiServer.construct(args);
+        resetFusekiMain();
+        return server;
+    }
+
+    private static void prepareFusekiMain() {
+        String fusekiBase = Lib.getenv(FusekiServerCtl.envFusekiBase);
+        if ( fusekiBase == null )
+            fusekiBase = FusekiServerCtl.dftFusekiBase;
+        FileOps.ensureDir(fusekiBase);
+
+        FusekiModules serverModules = serverModules();
+
+        // Adjust the default settings of ServerArgs
+        FusekiServerArgsCustomiser initializeServerArgs = new 
FusekiServerArgsCustomiser() {
+            @Override
+            public void serverArgsModify(CmdGeneral fusekiCmd, ServerArgs 
serverArgs) {
+                serverArgs.allowEmpty = true;
+                serverArgs.fusekiModules = serverModules;
+            }
+        };
+
+        FusekiMain.resetCustomisers();
+        FusekiMain.addCustomiser(initializeServerArgs);
+        // They can also modify the argument processing.
+        serverModules.forEach(FusekiMain::addCustomiser);
+    }
+
+    private static void resetFusekiMain() {
+        FusekiMain.resetCustomisers();
+    }
+
+    /** A use-once {@link FusekiModules} for the full-featured Fuseki server. 
*/
+    public static FusekiModules serverModules() {
+        // Modules may have state that is carried across the build steps or 
used for reload.
+        FusekiModule fmodShiro = FMod_Shiro.create();
+        FusekiModule fmodAdmin = FMod_Admin.create();
+        FusekiModule fmodUI = FMod_UI.create();
+        FusekiModule fmodPrometheus = FMod_Prometheus.create();
+
+        FusekiModules serverModules = FusekiModules.create(fmodAdmin, fmodUI, 
fmodShiro, fmodPrometheus);
+        return serverModules;
+    }
+}
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/admin/ArgModuleAdmin.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/admin/ArgModuleAdmin.java
deleted file mode 100644
index f1b9d6c751..0000000000
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/admin/ArgModuleAdmin.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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
- *
- *     http://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.
- */
-
-package org.apache.jena.fuseki.mod.admin;
-
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-import org.apache.jena.cmd.ArgDecl;
-import org.apache.jena.cmd.ArgModuleGeneral;
-import org.apache.jena.cmd.CmdArgModule;
-import org.apache.jena.cmd.CmdGeneral;
-import org.apache.jena.fuseki.FusekiConfigException;
-import org.apache.jena.fuseki.mgt.FusekiServerCtl;
-
-public class ArgModuleAdmin implements ArgModuleGeneral {
-    // Add a static of "extra command"
-
-    private ArgDecl argAdmin = new ArgDecl(true, "admin");
-    private ArgDecl argAdminArea = new ArgDecl(true, "adminArea", "adminBase");
-
-    public ArgModuleAdmin() { }
-
-    @Override
-    public void processArgs(CmdArgModule cmdLine) {
-        System.out.println("ArgModuleAdmin");
-        String admin = cmdLine.getValue(argAdmin);
-        if ( admin == null ) {
-            return;
-        }
-
-        if ( admin.equals("localhost") ) {}
-        else {
-            String pwFile = admin;
-        }
-
-        String dirStr = cmdLine.getValue(argAdminArea);
-        Path directory = Path.of(dirStr);
-
-        if ( ! Files.isDirectory(directory) )
-            throw new FusekiConfigException("Not a directory: "+dirStr);
-
-        if ( ! Files.isWritable(directory)  )
-            throw new FusekiConfigException("Not writable: "+dirStr);
-
-        FusekiServerCtl.FUSEKI_BASE = directory;
-    }
-
-    @Override
-    public void registerWith(CmdGeneral cmdLine) {
-        cmdLine.add(argAdmin,     "--admin=[UserPasswordFile|localhost]", 
"Enable the admin module");
-        cmdLine.add(argAdminArea, "--run=DIR", "Admin state directory");
-    }
-}
\ No newline at end of file
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/admin/FMod_Admin.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/admin/FMod_Admin.java
index d4ea62e661..c98e7b1628 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/admin/FMod_Admin.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/admin/FMod_Admin.java
@@ -69,8 +69,12 @@ public class FMod_Admin implements FusekiModule {
 
     private static Logger LOG = Fuseki.configLog;
 
-    private ArgDecl argAdmin = new ArgDecl(true, "admin");
-    private ArgDecl argAdminArea = new ArgDecl(true, "adminArea", "adminBase");
+    private static ArgDecl argAdmin = new ArgDecl(true, "admin");
+    private static ArgDecl argAdminArea = new ArgDecl(true, "adminArea", 
"adminBase");
+
+    // Module state.
+    private String admin = null;
+    private Path directory = null;
 
     @Override
     public void serverArgsModify(CmdGeneral fusekiCmd, ServerArgs serverArgs) {
@@ -80,6 +84,7 @@ public class FMod_Admin implements FusekiModule {
         ArgModuleGeneral argModule = new ArgModuleGeneral() {
             @Override
             public void registerWith(CmdGeneral cmdLine) {
+                // Phase 2
 //                cmdLine.add(argAdmin, "--admin", "Enable server admin with 
user:password");
 //                cmdLine.add(argAdminArea,"--adminRun", "Directory for server 
configuration");
             }
@@ -91,30 +96,30 @@ public class FMod_Admin implements FusekiModule {
 
     @Override
     public void serverArgsPrepare(CmdGeneral fusekiCmd, ServerArgs serverArgs) 
{
-        String admin = fusekiCmd.getValue(argAdmin);
-        if ( admin == null ) {
-            return;
-        }
-
-        Path directory = null;
         String dirStr = fusekiCmd.getValue(argAdminArea);
         if ( dirStr != null )
             directory = Path.of(dirStr);
 
         // Phase 2
-        if ( admin.equals("localhost") ) {}
-        else {
-            String pwFile = admin;
-        }
+//        String admin = fusekiCmd.getValue(argAdmin);
+//        if ( admin == null ) {
+//            return;
+//        }
+//
+//        if ( admin.equals("localhost") ) {}
+//        else {
+//            String pwFile = admin;
+//        }
 
         if ( directory != null ) {
             if ( ! Files.isDirectory(directory) )
                 throw new FusekiConfigException("Not a directory: "+dirStr);
-
             if ( ! Files.isWritable(directory)  )
                 throw new FusekiConfigException("Not writable: "+dirStr);
         }
-        FusekiServerCtl.FUSEKI_BASE = directory;
+
+        // "directory" null mean use the FUSEKI_BASE environment variable, 
done by "new FusekiServerCtl"
+
     }
 
 //    @Override
@@ -127,29 +132,27 @@ public class FMod_Admin implements FusekiModule {
     public void prepare(FusekiServer.Builder builder, Set<String> 
datasetNames, Model configModel) {
         // Ensure the work area is setup
 
-        Path path;
-        synchronized(FusekiServerCtl.class) {
-            // Temporary - one at a time because FUSEKI_BASE is static.
-            FusekiServerCtl app = new FusekiServerCtl(null);
-            path = app.setup();
-        }
-
-        FmtLog.info(LOG, "Fuseki Admin: %s", path);
+        FusekiServerCtl serverCtl = new FusekiServerCtl(directory);
+        serverCtl.setup();
+        Path fusekiBase = serverCtl.getFusekiBase();
+        builder.addServletAttribute(Fuseki.attrFusekiServerCtl, serverCtl);
+        FmtLog.info(LOG, "Fuseki Admin: %s", fusekiBase);
 
         // Shiro.
-        Path shiroIni = path.resolve(FusekiServerCtl.DFT_SHIRO_INI);
+        Path shiroIni = fusekiBase.resolve(FusekiServerCtl.DFT_SHIRO_INI);
         if ( Files.exists(shiroIni) ) {
             System.setProperty(FusekiServerCtl.envFusekiShiro, 
shiroIni.toString());
         } else {
-            FmtLog.info(LOG, "No shiro.ini: dir=%s", path);
+            FmtLog.info(LOG, "No shiro.ini: dir=%s", fusekiBase);
         }
 
         String configDir = FusekiServerCtl.dirConfiguration.toString();
         List<DataAccessPoint> directoryDatabases = 
FusekiConfig.readConfigurationDirectory(configDir);
 
-        if ( directoryDatabases.isEmpty() )
+        if ( directoryDatabases.isEmpty() && datasetNames.isEmpty() )
             FmtLog.info(LOG, "No databases: dir=%s", configDir);
         else {
+            datasetNames.forEach(n->FmtLog.info(Fuseki.configLog, "Database: 
%s", n));
             directoryDatabases.forEach(dap -> FmtLog.info(Fuseki.configLog, 
"Database: %s", dap.getName()));
         }
 
@@ -182,4 +185,9 @@ public class FMod_Admin implements FusekiModule {
                 .enableCompact(true)
                 ;
     }
+
+    // Currently, the server admin area does not move during the run of a 
server.
+    /** {@inheritDoc} */
+    @Override
+    public void serverReload(FusekiServer server) { }
 }
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/prometheus/FMod_Prometheus.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/prometheus/FMod_Prometheus.java
index c61a45fc05..6aa7eb6c7b 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/prometheus/FMod_Prometheus.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/prometheus/FMod_Prometheus.java
@@ -34,11 +34,12 @@ import org.apache.jena.rdf.model.Model;
  */
 public class FMod_Prometheus implements FusekiModule {
 
-    private static FusekiModule singleton = new FMod_Prometheus();
-    public static FusekiModule get() {
-        return singleton;
+    public static FusekiModule create() {
+        return new FMod_Prometheus();
     }
 
+    private MetricsProvider metricsProvider = null;
+
     public FMod_Prometheus() {}
 
 //    @Override
@@ -50,7 +51,7 @@ public class FMod_Prometheus implements FusekiModule {
     public String name() { return "FMod Prometheus Metrics"; }
 
     @Override public void prepare(FusekiServer.Builder serverBuilder, 
Set<String> datasetNames, Model configModel) {
-        MetricsProvider metricsProvider = new PrometheusMetricsProvider();
+        metricsProvider = new PrometheusMetricsProvider();
         serverBuilder.addServletAttribute(Fuseki.attrMetricsProvider, 
metricsProvider);
         serverBuilder.addServlet("/$/metrics", new ActionMetrics());
     }
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 ff0acf2fc4..646505c8b9 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
@@ -80,6 +80,7 @@ public class FMod_Shiro implements FusekiModule {
 
     private static ArgDecl argShiroIni = new ArgDecl(true, "shiro", 
"shiro-ini");
 
+    // Module state (for reload).
     private String shiroFile = null;
 
     public FMod_Shiro() {
@@ -107,12 +108,7 @@ public class FMod_Shiro implements FusekiModule {
 
     // The filter is added in prepare().
     // This allows other Fuseki modules, such as FMod_Admin, to setup 
shiro.ini.
-    // FMod_Admin unpacks a default one to $FUSEKI_BASE/shiro.ini (usually 
"run/shiro.ini")
-
-//    @Override
-//    public void serverArgsBuilder(FusekiServer.Builder serverBuilder, Model 
configModel) {
-//        //Add filter.
-//    }
+    // FMod_Admin unpacks a default one to FUSEKI_BASE/shiro.ini (usually 
"run/shiro.ini")
 
      /**
       * Determine the Shiro configuration file.
@@ -181,8 +177,10 @@ public class FMod_Shiro implements FusekiModule {
         }
     }
 
-    @Override
-    public void serverAfterStarting(FusekiServer server) {}
-
-// @Override public void serverStopped(FusekiServer server) { }
+    // 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) { }
 }
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FusekiShiroLib.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FusekiShiroLib.java
index c963771efc..a04b42589a 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FusekiShiroLib.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/shiro/FusekiShiroLib.java
@@ -23,6 +23,7 @@ import java.nio.file.Path;
 import java.util.List;
 
 import jakarta.servlet.ServletContext;
+import org.apache.jena.atlas.lib.IRILib;
 import org.apache.jena.fuseki.FusekiConfigException;
 import org.apache.jena.rfc3986.URIScheme;
 import org.apache.shiro.lang.io.ResourceUtils;
@@ -47,7 +48,9 @@ import org.apache.shiro.web.env.EnvironmentLoaderListener;
         for ( String loc : locations ) {
             // If file:, look for that file.
             if ( loc.startsWith(fileSchemePrefix) ) {
-                Path p = Path.of(loc.substring(fileSchemePrefix.length()));
+                // Convert (back) to a filesystem path.
+                String fn = IRILib.IRIToFilename(loc);
+                Path p = Path.of(fn);
                 if ( Files.exists(p) )
                     return loc;
                 // Ignore.
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/ShiroEnvironmentLoaderListener.java
index c99f47aee7..13295bb6e1 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/ShiroEnvironmentLoaderListener.java
@@ -69,9 +69,9 @@ class ShiroEnvironmentLoaderListener extends 
EnvironmentLoaderListener{
                 shiroConfigLog.info(format("No Shiro file found (tried: %s)", 
locations));
                 return;
             }
-            shiroConfigLog.info("Shiro INI: "+loc);
+            shiroConfigLog.info("Shiro file: "+loc);
             String[] configLocationsHere = new String[] {loc};
             env.setConfigLocations(configLocationsHere);
         }
     }
-}
\ No newline at end of file
+}
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/ui/FMod_UI.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/ui/FMod_UI.java
index 51ad0fa32d..2fb8b0de29 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/ui/FMod_UI.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/ui/FMod_UI.java
@@ -46,10 +46,9 @@ import org.slf4j.Logger;
 
 public class FMod_UI implements FusekiModule {
 
-    // Only one module needed - it is stateless.
     private static FusekiModule singleton = new FMod_UI();
-    public static FusekiModule get() {
-        return singleton;
+    public static FusekiModule create() {
+        return new FMod_UI();
     }
 
     public FMod_UI() {}
@@ -63,12 +62,15 @@ public class FMod_UI implements FusekiModule {
 //    }
 
     private static ArgDecl argUIFiles = new ArgDecl(true, "ui");
-    private String uiAppLocation = null;
+
     /** Java resource name used to find the UI files. */
     private static String resourceNameUI = "webapp";
-    /** Directory name of the root of UI files with {@code FUSEKI_BASE} */
+    /** Directory name of the root of UI files */
     private static String directoryNameUI = "webapp";
 
+    // UI resources location.
+    private String uiAppLocation = null;
+
     @Override
     public String name() {
         return "FMod UI";
@@ -95,8 +97,14 @@ public class FMod_UI implements FusekiModule {
             return;
         }
 
+        FusekiServerCtl serverCtl = 
(FusekiServerCtl)builder.getServletAttribute(Fuseki.attrFusekiServerCtl);
+        if ( serverCtl == null ) {
+            LOG.warn("No server control");
+            return;
+        }
+
         if ( uiAppLocation == null ) {
-            uiAppLocation = findFusekiApp();
+            uiAppLocation = findFusekiApp(serverCtl);
             if ( uiAppLocation == null ) {
                 LOG.warn("No Static content location has been found");
                 return;
@@ -114,26 +122,32 @@ public class FMod_UI implements FusekiModule {
         // LOG.info("Fuseki UI loaded");
     }
 
+    // Currently, fixed location during the run of a server.
+    /** {@inheritDoc} */
+    @Override
+    public void serverReload(FusekiServer server) { }
+
     /**
      * Locate the UI files.
      * <ol>
      * <li>Command line name of a directory</li>
-     * <li>{@code $FUSEKI_BASE/webapp}</li>
+     * <li>{@code FusekiServerCtl.getFusekibase()/webapp}</li>
      * <li>Classpath java resource {@code webapp}</li>
      * <ol>
      */
-    private String findFusekiApp() {
+    private String findFusekiApp(FusekiServerCtl serverCtl) {
         // 1:: Command line setting.
         if ( uiAppLocation != null )
             return uiAppLocation;
 
-        // 2:: $FUSEKI_BASE/webapp
-        // If the FUSEKI_BASE does not exists, it is created later in 
FMod_admin.prepare
-        // and does not include Fuseki app.
-        String x = fromPath(FusekiServerCtl.FUSEKI_BASE, directoryNameUI);
-        if ( x != null ) {
-            LOG.info("Fuseki UI - path resource: "+x);
-            return x;
+        // 2::FusekiServerCtl.getFusekibase()/webapp
+        if ( serverCtl != null ) {
+            Path fusekiBase =  serverCtl.getFusekiBase();
+            String x = fromPath(fusekiBase, directoryNameUI);
+            if ( x != null ) {
+                LOG.info("Fuseki UI - path resource: "+x);
+                return x;
+            }
         }
 
         // 3:: From a jar.
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/TS_FusekiMods.java
 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/TS_FusekiMods.java
index f4f261e0e9..982da56e2f 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/TS_FusekiMods.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/TS_FusekiMods.java
@@ -38,7 +38,8 @@ import org.apache.jena.fuseki.mod.shiro.TestModShiro;
     // Prometheus
     TestModPrometheus.class,
     // Apache Shiro
-    TestModShiro.class
+    TestModShiro.class,
+    TestFusekiServer.class
 })
 public class TS_FusekiMods {
     public TS_FusekiMods() {}
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/TestFusekiServer.java
 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/TestFusekiServer.java
index c9d11ed691..48140e0790 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/TestFusekiServer.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/TestFusekiServer.java
@@ -20,19 +20,77 @@ package org.apache.jena.fuseki.mod;
 
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
+import org.apache.jena.atlas.lib.FileOps;
 import org.apache.jena.fuseki.main.FusekiServer;
+import org.apache.jena.fuseki.main.cmds.FusekiMain;
+import org.apache.jena.fuseki.main.sys.FusekiModules;
+import org.apache.jena.fuseki.mgt.FusekiServerCtl;
+import org.apache.jena.fuseki.system.FusekiLogging;
+import org.apache.jena.http.HttpOp;
 
 /**
  * Test for the whole Fuseki server, not components.
  */
 public class TestFusekiServer {
-    @Test public void run() {
-        // Setup
-        FusekiServer server = FusekiModServer.runAsync("--port=0", "--empty");
-        int port = server.getPort();
-        assertNotEquals(0, port, "Port is zero after async start");
 
+    private static String serverBase =  "target/runBase";
+
+    @BeforeAll static void beforeAll() {
+        FusekiModules.restoreSystemDefault();
+        FusekiServerCtl.clearUpSystemState();
+        FusekiLogging.setLogging();
+    }
+
+    @AfterEach void afterEach() {
+        FusekiModules.restoreSystemDefault();
+        FusekiServerCtl.clearUpSystemState();
+    }
+
+    @Test
+    public void runCmdLine() {
+        String runBase = serverBase+"1";
+        setup(runBase);
+        // Build-run command line
+        FusekiServer server = FusekiServerRunner.runAsync("--port=0", 
"--empty");
+        try {
+            int port = server.getPort();
+            assertNotEquals(0, port, "Port is zero after async start");
+        } finally {
+            FusekiMain.resetCustomisers();
+            server.stop();
+            tearDown(runBase);
+        }
+    }
+
+    @Test
+    public void buildRun() {
+        String runBase = serverBase+"2";
+        setup(runBase);
+        // Build-run programmatically.
+        FusekiModules serverModules = FusekiServerRunner.serverModules();
+        FusekiServer server = 
FusekiServer.create().port(0).fusekiModules(serverModules).build();
+        server.start();
+        try {
+            int port = server.getPort();
+            assertNotEquals(0, port, "Port is zero after async start");
+            // check it has a UI.
+            HttpOp.httpGetString(server.serverURL()+"#");
+        } finally {
+            server.stop();
+            tearDown(runBase);
+        }
+    }
+
+    private void setup(String runBase) {
+        System.setProperty(FusekiServerCtl.envFusekiBase, runBase);
+        FileOps.clearAll(runBase);
+    }
+
+    private void tearDown(String runBase) {
+        FileOps.clearAll(runBase);
     }
 }
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/admin/TestAdmin.java
 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/admin/TestAdmin.java
index 396b14b289..fc73f76799 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/admin/TestAdmin.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/admin/TestAdmin.java
@@ -37,6 +37,7 @@ import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
 import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -59,6 +60,7 @@ import org.apache.jena.fuseki.main.sys.FusekiModules;
 import org.apache.jena.fuseki.mgt.FusekiServerCtl;
 import org.apache.jena.fuseki.mgt.ServerMgtConst;
 import org.apache.jena.fuseki.server.ServerConst;
+import org.apache.jena.fuseki.system.FusekiLogging;
 import org.apache.jena.fuseki.test.HttpTest;
 import org.apache.jena.riot.WebContent;
 import org.apache.jena.sparql.core.DatasetGraph;
@@ -88,6 +90,10 @@ public class TestAdmin {
     private String serverURL = null;
     private FusekiServer server = null;
 
+    @BeforeAll public static void logging() {
+        FusekiLogging.setLogging();
+    }
+
     @BeforeEach public void startServer() {
         System.setProperty("FUSEKI_BASE", "target/run");
         FileOps.clearAll("target/run");
@@ -120,8 +126,7 @@ public class TestAdmin {
         if ( server != null )
             server.stop();
         serverURL = null;
-        // Clearup FMod_Shiro.
-        System.getProperties().remove(FusekiServerCtl.envFusekiShiro);
+        FusekiServerCtl.clearUpSystemState();
     }
 
     protected String urlRoot() {
@@ -226,18 +231,17 @@ public class TestAdmin {
 
         try {
             Path f = Path.of(fileBase+"config-ds-plain-1.ttl");
-            {
-                httpPost(urlRoot()+"$/"+opDatasets,
-                         WebContent.contentTypeTurtle+"; 
charset="+WebContent.charsetUTF8,
-                         BodyPublishers.ofFile(f));
-            }
+            httpPost(urlRoot()+"$/"+opDatasets,
+                     WebContent.contentTypeTurtle+"; 
charset="+WebContent.charsetUTF8,
+                     BodyPublishers.ofFile(f));
             // Check exists.
             checkExists(dsTest);
+            // Try again.
             try {
-            } catch (HttpException ex) {
                 httpPost(urlRoot()+"$/"+opDatasets,
                          WebContent.contentTypeTurtle+"; 
charset="+WebContent.charsetUTF8,
                          BodyPublishers.ofFile(f));
+            } catch (HttpException ex) {
                 assertEquals(HttpSC.CONFLICT_409, ex.getStatusCode());
             }
         } catch (IOException ex) { IO.exception(ex); return; }
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/admin/TestTemplateAddDataset.java
 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/admin/TestTemplateAddDataset.java
index 9743802dcf..2035ae581c 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/admin/TestTemplateAddDataset.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/admin/TestTemplateAddDataset.java
@@ -85,7 +85,7 @@ public class TestTemplateAddDataset {
             server.stop();
         serverURL = null;
         // Clearup FMod_Shiro.
-        System.getProperties().remove(FusekiServerCtl.envFusekiShiro);
+        FusekiServerCtl.clearUpSystemState();
     }
 
     protected String urlRoot() {
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/metrics/TestModPrometheus.java
 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/metrics/TestModPrometheus.java
index dbb475b002..16bc61c017 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/metrics/TestModPrometheus.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/metrics/TestModPrometheus.java
@@ -48,7 +48,7 @@ public class TestModPrometheus {
 
     @BeforeEach void setupServer() {
         DatasetGraph dsg = DatasetGraphFactory.createTxnMem();
-        FusekiModules fusekiModules = 
FusekiModules.create(FMod_Prometheus.get());
+        FusekiModules fusekiModules = 
FusekiModules.create(FMod_Prometheus.create());
         testServer = FusekiServer.create()
                 .add("/ds", dsg)
                 .enableMetrics(false)       // N.B. false. Instead, use module 
to setup.
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/shiro/TestModShiro.java
 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/shiro/TestModShiro.java
index 48f064f6a7..3a740012a2 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/shiro/TestModShiro.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/shiro/TestModShiro.java
@@ -26,7 +26,6 @@ import java.net.Authenticator;
 import java.net.http.HttpClient;
 import java.util.regex.Pattern;
 
-import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -55,23 +54,20 @@ public class TestModShiro {
 
     static {
         FusekiLogging.setLogging();
-        // Incase it is finding file:log4j.properties first.
+        // In case it is finding file:log4j.properties first.
 //        LogCtl.disable(Fuseki.serverLog);
 //        LogCtl.disable(Fuseki.actionLog);
 //        LogCtl.disable(FMod_Shiro.shiroConfigLog);
     }
 
     @BeforeEach void before() {
-        System.getProperties().remove(FusekiServerCtl.envFusekiShiro);
+        FusekiServerCtl.clearUpSystemState();
         AuthEnv.get().clearAuthEnv();
     }
 
     @AfterEach void after() {
         AuthEnv.get().clearAuthEnv();
-    }
-
-    @AfterAll static void afterAll() {
-        System.getProperties().remove(FusekiServerCtl.envFusekiShiro);
+        FusekiServerCtl.clearUpSystemState();
     }
 
     private String unlocalhost(FusekiServer server, String dataset) {
@@ -108,7 +104,6 @@ public class TestModShiro {
             attemptByLocalhost(server, dsLocal);
         } finally {
             server.stop();
-            AuthEnv.get().clearAuthEnv();
         }
     }
 
@@ -164,7 +159,6 @@ public class TestModShiro {
 
         } finally {
             server.stop();
-            AuthEnv.get().clearAuthEnv();
         }
     }
 
diff --git a/jena-fuseki2/pom.xml b/jena-fuseki2/pom.xml
index 18844744cf..8e2c5d75d4 100644
--- a/jena-fuseki2/pom.xml
+++ b/jena-fuseki2/pom.xml
@@ -57,8 +57,7 @@
     <!-- Fuseki main server -->
     <module>jena-fuseki-main</module>
 
-    <!-- Fuseki modules for Fuseki main-->
-    <!-- ... -->
+    <!-- fuseki-server.jar -->
     <module>jena-fuseki-server</module>
     
     <!-- Fuseki Webapp -->

Reply via email to