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 bc2ad4323829912e9a8ee66490dfa0e0e1c7226c
Author: Andy Seaborne <[email protected]>
AuthorDate: Fri Jul 25 19:40:00 2025 +0100

    GH-3348: FusekiServerRunner and FusekiMainRunner
---
 .../main/java/org/apache/jena/fuseki/Fuseki.java   |  18 ++--
 .../jena-fuseki-main/src/main/java/fuseki/run.java |  10 +-
 .../apache/jena/fuseki/main/FusekiMainRunner.java  |  80 ++++++++++++++
 .../org/apache/jena/fuseki/main/FusekiServer.java  |  16 ++-
 .../apache/jena/fuseki/main/cmds/FusekiMain.java   |   5 +-
 .../jena/fuseki/main/cmds/FusekiServerCmd.java     |   8 +-
 .../main/sys/FusekiServerArgsCustomiser.java       |  10 +-
 .../jena/fuseki/mod/FusekiServerModules.java       |  42 ++++++++
 .../apache/jena/fuseki/mod/FusekiServerRunner.java |  94 +++--------------
 .../apache/jena/fuseki/mod/shiro/FMod_Shiro.java   |   4 -
 .../fuseki/{mod => server}/FusekiServerRunner.java |  68 ++++++------
 .../org/apache/jena/fuseki/TC_FusekiServer.java    |   4 +-
 .../apache/jena/fuseki/main/TestConfigFile.java    |   2 +-
 .../jena/fuseki/main/TestCrossOriginFilter.java    |   6 +-
 .../org/apache/jena/fuseki/mod/TS_FusekiMods.java  |   8 +-
 .../fuseki/mod/admin/FusekiServerPerTestClass.java |   9 +-
 .../TS_FusekiServer.java}                          |  14 +--
 .../fuseki/{mod => server}/TestFusekiServer.java   |   5 +-
 .../jena/fuseki/server/TestFusekiServerCmd.java    | 117 +++++++++++++++++++++
 .../org/apache/jena/fuseki/cmd/FusekiArgs.java     |   3 +-
 20 files changed, 351 insertions(+), 172 deletions(-)

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 beca3d7f2a..e7a996dd11 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
@@ -169,23 +169,21 @@ public class Fuseki {
     public static final String        compactLogName    = PATH + ".Compact";
     public static final Logger        compactLog        = 
LoggerFactory.getLogger(compactLogName);;
 
-    /** Instance of log for config server messages.
-     * This is the global default used to set attribute
-     * in each server created.
-     */
-    public static boolean             verboseLogging    = false;
-
-    // Servlet context attribute names,
-
+    // Servlet context attribute names used by the core engine.
+    // Also in FusekiServerCtl for Fuseki server
     public static final String attrVerbose                 = 
"org.apache.jena.fuseki:verbose";
     public static final String attrNameRegistry            = 
"org.apache.jena.fuseki:DataAccessPointRegistry";
     public static final String attrOperationRegistry       = 
"org.apache.jena.fuseki:OperationRegistry";
+    public static final String attrMetricsProvider         = 
"org.apache.jena.fuseki:MetricsProvider";
+
+    // Use by jena-fuseki-access
     public static final String attrAuthorizationService    = 
"org.apache.jena.fuseki:AuthorizationService";
-    // The Fuseki Server
+
+    // Servlet context attribute names used by Fuseki Server.
+    // The server (so we can go from servlet context, available in request, to 
the 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) {
         cxt.setAttribute(attrVerbose, Boolean.valueOf(verbose));
diff --git a/jena-fuseki2/jena-fuseki-main/src/main/java/fuseki/run.java 
b/jena-fuseki2/jena-fuseki-main/src/main/java/fuseki/run.java
index 78622d174f..195ce8464a 100644
--- a/jena-fuseki2/jena-fuseki-main/src/main/java/fuseki/run.java
+++ b/jena-fuseki2/jena-fuseki-main/src/main/java/fuseki/run.java
@@ -18,16 +18,16 @@
 
 package fuseki;
 
+import org.apache.jena.fuseki.main.FusekiMainRunner;
+
 /**
  * Run FusekiMain.
+ * @deprecated Use {@link FusekiMainRunner#run}.
  */
+@Deprecated(forRemoval = true)
 public class run {
-    // This class must not depend on any Jena code (it would trigger
-    // early logging initialization) except FusekiLogging.setLogging.
-    //static { FusekiLogging.setLogging(); }
-
     public static void main (String... args) {
         // This does FusekiLogging.setLogging
-        org.apache.jena.fuseki.main.cmds.FusekiMainCmd.main(args);
+        FusekiMainRunner.run(args);
     }
 }
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiMainRunner.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiMainRunner.java
new file mode 100644
index 0000000000..9a1da1843a
--- /dev/null
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiMainRunner.java
@@ -0,0 +1,80 @@
+/*
+ * 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 java.net.BindException;
+
+import org.apache.jena.fuseki.Fuseki;
+import org.apache.jena.fuseki.FusekiException;
+import org.apache.jena.fuseki.main.cmds.FusekiMain;
+import org.apache.jena.fuseki.main.sys.FusekiModules;
+
+/**
+ * Functions for building and runner a {@link FusekiServer} configured from 
command line arguments.
+ *
+ * @see {@link FusekiServerRunner0} for similar functionality except it is 
also initialized with {@link FusekiModules} as well.
+ */
+public class FusekiMainRunner {
+
+    /**
+     * Run a plain {@link FusekiServer}.
+     * @param Command line arguments.
+     * @return Return the running server.
+     */
+    public static FusekiServer runAsync(String... args) {
+        FusekiServer server = construct(args);
+        try {
+            return server.start();
+        } catch (FusekiException ex) {
+            if ( ex.getCause() instanceof BindException ) {
+                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);
+        }
+    }
+
+    /**
+     * Run a plain {@link FusekiServer}.
+     * This function does not return.
+     */
+    public static void run(String... args) {
+        FusekiServer server = runAsync(args);
+        server.join();
+    }
+
+    /**
+     * Build but do not start, a {@link FusekiServer}.
+     */
+    public static FusekiServer construct(String... args) {
+        FusekiServer.Builder builder = builder(args);
+        return builder.build();
+    }
+
+    /**
+     * Create a {@link FusekiServer.Builder}
+     * initialized according to the command line arguments processed.
+     */
+    public static FusekiServer.Builder builder(String... args) {
+        return FusekiMain.builder(args);
+    }
+
+}
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java
index 8f8cb9991c..1520b16862 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java
@@ -118,9 +118,11 @@ public class FusekiServer {
     /**
      * Construct a Fuseki server from command line arguments.
      * The return server has not been started.
+     * @deprecated Use {@link FusekiMainRunner#construct} or {@link 
FusekiServerRunner#construct} 
      */
+    @Deprecated
     static public FusekiServer construct(String... args) {
-        return FusekiMain.build(args);
+        return FusekiMainRunner.construct(args);
     }
 
     /** Construct a Fuseki server for one dataset.
@@ -420,9 +422,17 @@ public class FusekiServer {
         } catch (Exception e) { throw new FusekiException(e); }
     }
 
-    /** Wait for the server to exit. This call is blocking. */
+    /**
+     * Wait for the server to exit.
+     * This call starts the server if it has not already been started.
+     * This call is blocking and does not return unless there is an error.
+     */
     public void join() {
-        try { server.join(); }
+        try {
+            if ( ! server.isStarted() && ! server.isStarting() )
+                server.start();
+            server.join(); }
+        catch (FusekiException e) { throw e; }
         catch (Exception e) { throw new FusekiException(e); }
     }
 
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 7a2f85939c..b709b3ab53 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
@@ -28,6 +28,8 @@ import java.util.Objects;
 import java.util.function.Consumer;
 import java.util.stream.Stream;
 
+import arq.cmdline.CmdARQ;
+import arq.cmdline.ModDatasetAssembler;
 import org.apache.jena.assembler.exceptions.AssemblerException;
 import org.apache.jena.atlas.io.IOX;
 import org.apache.jena.atlas.lib.FileOps;
@@ -52,9 +54,6 @@ import org.apache.jena.sparql.core.assembler.AssemblerUtils;
 import org.apache.jena.sys.JenaSystem;
 import org.slf4j.Logger;
 
-import arq.cmdline.CmdARQ;
-import arq.cmdline.ModDatasetAssembler;
-
 public class FusekiMain extends CmdARQ {
 
     /** Default HTTP port when running from the command line. */
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 1f4dc87fe7..a5a305099c 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,10 +18,12 @@
 
 package org.apache.jena.fuseki.main.cmds;
 
-import org.apache.jena.fuseki.mod.FusekiServerRunner;
+import org.apache.jena.fuseki.main.sys.FusekiModule;
+import org.apache.jena.fuseki.server.FusekiServerRunner;
 import org.apache.jena.fuseki.system.FusekiLogging;
 
-/** Fuseki command that runs a Fuseki server with the admin UI.
+/**
+ * Fuseki command that runs a Fuseki server with the admin UI and other {@link 
FusekiModule FusekiModules}.
  * <p>
  * Use {@code --conf=} for multiple datasets and specific service names.
  * <p>
@@ -45,7 +47,7 @@ public class FusekiServerCmd {
      * syntax but not start it.
      */
     static public void main(String... args) {
-        FusekiServerRunner.main(args);
+        FusekiServerRunner.construct(args).join();
     }
 }
 
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiServerArgsCustomiser.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiServerArgsCustomiser.java
index e3abd58165..275a61877b 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiServerArgsCustomiser.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiServerArgsCustomiser.java
@@ -25,8 +25,6 @@ import org.apache.jena.fuseki.main.cmds.FusekiMain;
 import org.apache.jena.fuseki.main.cmds.ServerArgs;
 import org.apache.jena.rdf.model.Model;
 
-
-
 /**
  * Interface to implement for extending the CLI argument parsing portion of a
  * {@link FusekiServer}.
@@ -46,16 +44,16 @@ import org.apache.jena.rdf.model.Model;
  *   {@link FusekiAutoModule#start()} or from Java application code.
  * </li>
  * <li>
- *    {@link #serverArgsModify} &mdash; Register or modify the argument setup 
to be
+ *    {@link #serverArgsModify} &ndash; Register or modify the argument setup 
to be
  *     used to parse the command line.
  * </li>
  * <li>
- *   {@link #serverArgsPrepare} &mdash; Called after parsing the command line 
and
+ *   {@link #serverArgsPrepare} &ndash; Called after parsing the command line 
and
  *   recoding the command line settings in {@link ServerArgs}. Customisers can 
record
  *   argument values and flags.
  * </li>
  * <li>
- *   {@link #serverArgsBuilder} &mdash; Called after the {@link ServerArgs} 
have
+ *   {@link #serverArgsBuilder} &ndash; Called after the {@link ServerArgs} 
have
  *   been used to construct a server builder.
  * </li>
  * </ul>
@@ -76,7 +74,7 @@ public interface FusekiServerArgsCustomiser {
      * The server construction is aborted.
      *
      * @param fusekiCmd Fuseki Main command line arguments
-     * @param serverArgs Intial setting before command line processing.
+     * @param serverArgs Initial setting before command line processing.
      */
     public default void serverArgsModify(CmdGeneral fusekiCmd, ServerArgs 
serverArgs) { }
 
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/FusekiServerModules.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/FusekiServerModules.java
new file mode 100644
index 0000000000..292e641d65
--- /dev/null
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/FusekiServerModules.java
@@ -0,0 +1,42 @@
+/*
+ * 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 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 FusekiServerModules {
+
+    /** 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/FusekiServerRunner.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/FusekiServerRunner.java
index fc4a158166..c8797c4c22 100644
--- 
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
@@ -18,102 +18,38 @@
 
 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;
 
+/**
+ *  @deprecated Use from new locations
+ */
+@Deprecated(forRemoval = true)
 public class FusekiServerRunner {
 
-    public static void main(String... args) {
-        prepareFusekiServerConstruct();
-        FusekiMain.run(args);
-        // Does not return.
-    }
-
     /**
      * Run {@link FusekiServer} with {@link FusekiModules} as given by {@link 
#serverModules()}.
+     *  @deprecated Use from new location {@link 
org.apache.jena.fuseki.server.FusekiServerRunner#runASync}.
      */
+    @Deprecated(forRemoval = true)
     public static FusekiServer runAsync(String... args) {
-        FusekiServer server = construct(args);
-        try {
-            return 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 org.apache.jena.fuseki.server.FusekiServerRunner.runAsync(args);
     }
 
     /**
      * Build but do not start, a {@link FusekiServer} with {@link 
FusekiModules} as given by {@link #serverModules()}.
+     *  @deprecated Use from new location {@link 
org.apache.jena.fuseki.server.FusekiServerRunner#construct}.
      */
+    @Deprecated(forRemoval = true)
     public static FusekiServer construct(String... args) {
-        prepareFusekiServerConstruct();
-        // Make server
-        FusekiServer server = FusekiServer.construct(args);
-        resetFusekiMain();
-        return server;
-    }
-
-    private static void prepareFusekiServerConstruct() {
-        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();
+        return 
org.apache.jena.fuseki.server.FusekiServerRunner.construct(args);
     }
 
-    /** A use-once {@link FusekiModules} for the full-featured Fuseki server. 
*/
+    /**
+     * @deprecated Use from new location {@link 
org.apache.jena.fuseki.server.FusekiServerModules#serverModules}.
+     */
+    @Deprecated(forRemoval = true)
     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;
+        return org.apache.jena.fuseki.mod.FusekiServerModules.serverModules();
     }
 }
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 add556c272..8746846bad 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
@@ -70,12 +70,8 @@ public class FMod_Shiro implements FusekiModule {
         return new FMod_Shiro();
     }
 
-    // Assumes the whole system is "Shiro".
-    // No setup?
-
     public static final Logger shiroConfigLog = 
LoggerFactory.getLogger(Fuseki.PATH + ".Shiro");
 
-    private static List<String> defaultIniFileLocations = 
List.of("file:shiro.ini", "file:/etc/fuseki/shiro.ini");
     private static List<String> iniFileLocations = null;
 
     private static ArgDecl argShiroIni = new ArgDecl(true, "shiro", 
"shiro-ini");
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/server/FusekiServerRunner.java
similarity index 68%
copy from 
jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/mod/FusekiServerRunner.java
copy to 
jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/server/FusekiServerRunner.java
index fc4a158166..30212b4d6c 100644
--- 
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/server/FusekiServerRunner.java
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-package org.apache.jena.fuseki.mod;
+package org.apache.jena.fuseki.server;
 
 import java.net.BindException;
 
@@ -25,28 +25,27 @@ 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.FusekiMainRunner;
 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;
+import org.apache.jena.fuseki.mod.FusekiServerModules;
 
+/**
+ * Functions for building and runner a {@link FusekiServer} configured from 
command line arguments
+ * and system {@link FusekiModules}.
+ *
+ * @see {@link FusekiMainRunner} for similar functionality except without the 
configuring with {@link FusekiModules}.
+ */
 public class FusekiServerRunner {
 
-    public static void main(String... args) {
-        prepareFusekiServerConstruct();
-        FusekiMain.run(args);
-        // Does not return.
-    }
-
     /**
      * Run {@link FusekiServer} with {@link FusekiModules} as given by {@link 
#serverModules()}.
+     * @param Command line arguments.
+     * @return Return the running server.
      */
     public static FusekiServer runAsync(String... args) {
         FusekiServer server = construct(args);
@@ -54,10 +53,6 @@ public class FusekiServerRunner {
             return 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);
             }
@@ -67,15 +62,34 @@ public class FusekiServerRunner {
         }
     }
 
+    /**
+     * Run {@link FusekiServer} with {@link FusekiModules} as given by {@link 
#serverModules()}.
+     * This function does not return.
+     */
+    public static void run(String... args) {
+        FusekiServer server = runAsync(args);
+        server.join();
+    }
+
     /**
      * Build but do not start, a {@link FusekiServer} with {@link 
FusekiModules} as given by {@link #serverModules()}.
      */
     public static FusekiServer construct(String... args) {
+        FusekiServer.Builder builder = builder(args);
+        return builder.build();
+    }
+
+    /**
+     * Create a {@link FusekiServer.Builder} that has the FusekiServer with
+     * server modules setup and the command line args processed.
+     */
+    public static FusekiServer.Builder builder(String... args) {
         prepareFusekiServerConstruct();
-        // Make server
-        FusekiServer server = FusekiServer.construct(args);
-        resetFusekiMain();
-        return server;
+        try {
+            return FusekiMain.builder(args);
+        } finally {
+            resetFusekiMain();
+        }
     }
 
     private static void prepareFusekiServerConstruct() {
@@ -84,7 +98,7 @@ public class FusekiServerRunner {
             fusekiBase = FusekiServerCtl.dftFusekiBase;
         FileOps.ensureDir(fusekiBase);
 
-        FusekiModules serverModules = serverModules();
+        FusekiModules serverModules = FusekiServerModules.serverModules();
 
         // Adjust the default settings of ServerArgs
         FusekiServerArgsCustomiser initializeServerArgs = new 
FusekiServerArgsCustomiser() {
@@ -104,16 +118,4 @@ public class FusekiServerRunner {
     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/test/java/org/apache/jena/fuseki/TC_FusekiServer.java
 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/TC_FusekiServer.java
index 8396f202fb..3bd654ade4 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/TC_FusekiServer.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/TC_FusekiServer.java
@@ -24,11 +24,13 @@ import org.junit.platform.suite.api.Suite;
 import org.apache.jena.fuseki.main.TS_FusekiMain;
 import org.apache.jena.fuseki.main.access.TS_SecurityFuseki;
 import org.apache.jena.fuseki.mod.TS_FusekiMods;
+import org.apache.jena.fuseki.server.TS_FusekiServer;
 
 @Suite
 @SelectClasses({
     TS_FusekiMain.class,
     TS_SecurityFuseki.class,
-    TS_FusekiMods.class
+    TS_FusekiMods.class,
+    TS_FusekiServer.class
 })
 public class TC_FusekiServer {}
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestConfigFile.java
 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestConfigFile.java
index 5617b63f9d..a46580678c 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestConfigFile.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestConfigFile.java
@@ -325,7 +325,7 @@ public class TestConfigFile {
     // Several types of Model.
     // Requires that the configuration file has had the assembler 
registrations added correctly.
     @Test public void setupDatasetOfModels() {
-        FusekiServer server = FusekiServer.construct("--port=0", "--conf", 
DIR+"configOfModels.ttl");
+        FusekiServer server = FusekiMainRunner.construct("--port=0", "--conf", 
DIR+"configOfModels.ttl");
         server.start();
         server.stop();
     }
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestCrossOriginFilter.java
 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestCrossOriginFilter.java
index 26daae5826..a3702f6eef 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestCrossOriginFilter.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestCrossOriginFilter.java
@@ -75,7 +75,7 @@ public class TestCrossOriginFilter {
     }
 
     private static FusekiServer server(String ...args) {
-        return FusekiServer.construct(args);
+        return FusekiMainRunner.construct(args);
     }
 
     private static void executeWithServer(FusekiServer server, String 
datasetName, Consumer<String> action) {
@@ -88,7 +88,7 @@ public class TestCrossOriginFilter {
 
     @Test
     public void test_corsWithOrigin() {
-        FusekiServer server = FusekiServer.construct("-v", "--port=0", 
"--mem", "/ds");
+        FusekiServer server = FusekiMainRunner.construct("-v", "--port=0", 
"--mem", "/ds");
         executeWithServer(server, "/ds", URL->{
             // Default responses.
             String originString = "https://test.example.org/";;
@@ -121,7 +121,7 @@ public class TestCrossOriginFilter {
 
     @Test
     public void test_corsLocalhost() {
-        FusekiServer server = FusekiServer.construct("-v", "--port=0", 
"--mem", "/ds");
+        FusekiServer server = FusekiMainRunner.construct("-v", "--port=0", 
"--mem", "/ds");
         executeWithServer(server, "/ds", URL->{
             HttpResponse<InputStream> response = httpOptions(URL,
                                                             //"Host", 
"test.example.com"
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 b8d8ddf482..48c5cb2b2b 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
@@ -18,11 +18,12 @@
 
 package org.apache.jena.fuseki.mod;
 
+import org.junit.platform.suite.api.SelectClasses;
+import org.junit.platform.suite.api.Suite;
+
 import org.apache.jena.fuseki.mod.admin.TSMod_Admin;
 import org.apache.jena.fuseki.mod.metrics.TestModPrometheus;
 import org.apache.jena.fuseki.mod.shiro.TestModShiro;
-import org.junit.platform.suite.api.SelectClasses;
-import org.junit.platform.suite.api.Suite;
 
 @Suite
 @SelectClasses({
@@ -36,8 +37,7 @@ import org.junit.platform.suite.api.Suite;
     // Shiro
     TestModShiro.class,
 
-    // Whole server
-    TestFusekiServer.class
+    // Integrated server tested in TS_FusekiServer.class
 })
 public class TS_FusekiMods {
     public TS_FusekiMods() {}
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/admin/FusekiServerPerTestClass.java
 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/admin/FusekiServerPerTestClass.java
index 5f1cbc35c7..fc761b6a9f 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/admin/FusekiServerPerTestClass.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/admin/FusekiServerPerTestClass.java
@@ -23,6 +23,9 @@ import static org.junit.jupiter.api.Assertions.fail;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+
 import org.apache.jena.atlas.lib.FileOps;
 import org.apache.jena.atlas.logging.LogCtl;
 import org.apache.jena.fuseki.Fuseki;
@@ -30,14 +33,12 @@ import org.apache.jena.fuseki.ctl.ActionSleep;
 import org.apache.jena.fuseki.main.FusekiServer;
 import org.apache.jena.fuseki.main.sys.FusekiModules;
 import org.apache.jena.fuseki.mgt.FusekiServerCtl;
-import org.apache.jena.fuseki.mod.FusekiServerRunner;
+import org.apache.jena.fuseki.mod.FusekiServerModules;
 import org.apache.jena.fuseki.server.DataAccessPointRegistry;
 import org.apache.jena.fuseki.system.FusekiLogging;
 import org.apache.jena.sparql.core.DatasetGraph;
 import org.apache.jena.sparql.core.DatasetGraphFactory;
 import org.awaitility.Awaitility;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
 
 /**
  * Framework for running tests on a Fuseki server, with a single server for 
all tests.
@@ -88,7 +89,7 @@ public class FusekiServerPerTestClass {
 
     // For the one-per-class setup, include the usual modules for 
jena-fuseki-server.
     private static FusekiModules modulesSetup() {
-        return FusekiServerRunner.serverModules();
+        return FusekiServerModules.serverModules();
     }
 
     private static FusekiServer createServerForTest() {
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/TC_FusekiServer.java
 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/server/TS_FusekiServer.java
similarity index 75%
copy from 
jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/TC_FusekiServer.java
copy to 
jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/server/TS_FusekiServer.java
index 8396f202fb..bc44ce393a 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/TC_FusekiServer.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/server/TS_FusekiServer.java
@@ -16,19 +16,15 @@
  * limitations under the License.
  */
 
-package org.apache.jena.fuseki;
+package org.apache.jena.fuseki.server;
 
 import org.junit.platform.suite.api.SelectClasses;
 import org.junit.platform.suite.api.Suite;
 
-import org.apache.jena.fuseki.main.TS_FusekiMain;
-import org.apache.jena.fuseki.main.access.TS_SecurityFuseki;
-import org.apache.jena.fuseki.mod.TS_FusekiMods;
-
 @Suite
 @SelectClasses({
-    TS_FusekiMain.class,
-    TS_SecurityFuseki.class,
-    TS_FusekiMods.class
+    TestFusekiServer.class,
+    TestFusekiServerCmd.class
 })
-public class TC_FusekiServer {}
+
+public class TS_FusekiServer {}
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/server/TestFusekiServer.java
similarity index 94%
rename from 
jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/mod/TestFusekiServer.java
rename to 
jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/server/TestFusekiServer.java
index 48140e0790..c16eb08f56 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/server/TestFusekiServer.java
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-package org.apache.jena.fuseki.mod;
+package org.apache.jena.fuseki.server;
 
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 
@@ -29,6 +29,7 @@ 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.mod.FusekiServerModules;
 import org.apache.jena.fuseki.system.FusekiLogging;
 import org.apache.jena.http.HttpOp;
 
@@ -71,7 +72,7 @@ public class TestFusekiServer {
         String runBase = serverBase+"2";
         setup(runBase);
         // Build-run programmatically.
-        FusekiModules serverModules = FusekiServerRunner.serverModules();
+        FusekiModules serverModules = FusekiServerModules.serverModules();
         FusekiServer server = 
FusekiServer.create().port(0).fusekiModules(serverModules).build();
         server.start();
         try {
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/server/TestFusekiServerCmd.java
 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/server/TestFusekiServerCmd.java
new file mode 100644
index 0000000000..52dd4f6c61
--- /dev/null
+++ 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/server/TestFusekiServerCmd.java
@@ -0,0 +1,117 @@
+/*
+ * 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.server;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.jena.atlas.lib.Lib;
+import org.apache.jena.fuseki.FusekiConfigException;
+import org.apache.jena.fuseki.main.FusekiServer;
+import org.apache.jena.fuseki.mgt.FusekiServerCtl;
+import org.apache.jena.http.HttpOp;
+import org.apache.jena.sparql.exec.http.Params;
+
+/**
+ * Tests Fuseki Server run with all features (FMods)from the command line.
+ */
+public class TestFusekiServerCmd {
+
+    static String basearea = "target/run";
+    static {
+        Lib.setenv("FUSEKI_BASE", basearea);
+    }
+
+    private static void deleteFusekiDir() throws IOException {
+        File file = new File(basearea);
+        if ( file.exists() )
+            FileUtils.deleteDirectory(file);
+    }
+
+    @BeforeAll static void beforeAll() throws IOException {
+        deleteFusekiDir();
+    }
+
+    @BeforeEach void setup() throws IOException {
+        clearUp();
+    }
+
+    @AfterAll static void afterAll() throws IOException {
+        clearUp();
+    }
+
+    static void clearUp() throws IOException {
+        deleteFusekiDir();
+        FusekiServerCtl.clearUpSystemState();
+    }
+
+    @Test public void plainStart() {
+        FusekiServer server = FusekiServerRunner.construct();
+        server.start();
+        server.stop();
+    }
+
+    @Test public void persistentConfigurationBlockCommandLine() {
+        // Create a persistent configuration.
+
+        String dbName = "/ds93" ;
+
+        FusekiServer server0 = FusekiServerRunner.construct();
+        server0.start();
+        addDataset(server0, dbName);
+        server0.stop();
+
+        // Check run area?
+
+        // Name clash.
+        assertThrows(FusekiConfigException.class, ()-> 
start_stop_server("--mem", dbName) );
+
+        // And no args is fine.
+        start_stop_server();
+
+        // Different database
+        start_stop_server("--mem", "/ds99");
+    }
+
+
+    private void addDataset(FusekiServer server, String dbName) {
+        String serverURL = server.serverURL();
+        assertNotNull(serverURL);
+        String actionDataset = serverURL+"$/datasets";
+        String datasetURL = server.datasetURL(dbName);
+        Params params = Params.create().add("dbName", dbName).add("dbType", 
"mem");
+        // Use the template form of adding a dataset.s
+        HttpOp.httpPostForm(actionDataset, params);
+    }
+
+    private void start_stop_server(String... args) {
+        FusekiServer server = FusekiServerRunner.construct(args);
+        server.start();
+        server.stop();
+    }
+}
diff --git 
a/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/cmd/FusekiArgs.java
 
b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/cmd/FusekiArgs.java
index 91c96ad7a4..7078c6806a 100644
--- 
a/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/cmd/FusekiArgs.java
+++ 
b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/cmd/FusekiArgs.java
@@ -21,13 +21,12 @@ package org.apache.jena.fuseki.cmd;
 import java.util.HashMap;
 import java.util.Map;
 
-import org.apache.jena.fuseki.Fuseki;
 import org.apache.jena.sparql.core.DatasetGraph;
 
 /** Dataset setup (command line, config file) for a dataset (or several if 
config file) */
 public class FusekiArgs {
     public boolean quiet = false;
-    public boolean verbose = Fuseki.verboseLogging;
+    public boolean verbose = false;
 
     // Priority order : --conf, templated
     // through the command line processing should not allow --conf and a 
templated /dataset.


Reply via email to