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 0492ac64aead3ed989b752c344189eece5884e67
Author: Andy Seaborne <[email protected]>
AuthorDate: Fri Nov 15 09:32:31 2024 +0000

    GH-2753: Refactor FusekiMain
---
 .../org/apache/jena/fuseki/main/FusekiServer.java  |   2 +-
 .../jena/fuseki/main/cmds/ArgCustomisers.java      |  71 ++++
 .../org/apache/jena/fuseki/main/cmds/DSGSetup.java |  69 +++-
 .../apache/jena/fuseki/main/cmds/FusekiMain.java   | 420 +++++++++++----------
 .../apache/jena/fuseki/main/cmds/ServerArgs.java   |  76 ++--
 .../jena/fuseki/main/sys/FusekiAutoModules.java    |  36 +-
 .../apache/jena/fuseki/main/sys/FusekiModules.java |  43 ++-
 .../main/sys/FusekiServerArgsCustomiser.java       |  13 +-
 .../apache/jena/fuseki/main/sys/InitFuseki.java    |   8 +-
 .../fuseki/main/TestFusekiMainCmdArguments.java    |  41 +-
 .../main/TestFusekiMainCmdCustomArguments.java     | 104 +++--
 .../jena/fuseki/main/sys/TestFusekiModules.java    |  36 +-
 12 files changed, 600 insertions(+), 319 deletions(-)

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 ef3c4622bd..40b968e9f2 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
@@ -1317,7 +1317,7 @@ public class FusekiServer {
                 serverHttpPort = DefaultServerPort;
 
             FusekiModules modules = (fusekiModules == null)
-                    ? FusekiAutoModules.load()
+                    ? FusekiModules.getSystemModules()
                     : fusekiModules;
 
             // FusekiModule call - final preparations.
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/ArgCustomisers.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/ArgCustomisers.java
new file mode 100644
index 0000000000..c83b2fc1de
--- /dev/null
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/ArgCustomisers.java
@@ -0,0 +1,71 @@
+/*
+ * 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.cmds;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Supplier;
+
+import org.apache.jena.fuseki.main.sys.FusekiModule;
+import org.apache.jena.fuseki.main.sys.FusekiModules;
+import org.apache.jena.fuseki.main.sys.FusekiServerArgsCustomiser;
+
+/**
+ * The list of {@link FusekiServerArgsCustomiser} used by FusekiMain.
+ *
+ */
+class ArgCustomizers {
+    private ArgCustomizers(){}
+
+    // Null means "use system modules"
+    static private List<FusekiServerArgsCustomiser> customisers = null;
+
+    /*package*/ static void addCustomiser(FusekiServerArgsCustomiser 
customiser) {
+        Objects.requireNonNull(customiser);
+        if ( customisers == null )
+            customisers = new ArrayList<>();
+        customisers.add(customiser);
+    }
+
+    /**
+     * Remove any previously registered CLI customisers
+     */
+    /*package*/ static void resetCustomisers() {
+        // Goes back to supplying the system modules.
+        customisers = null;
+    }
+
+    /**
+     * Return the current setting of
+     * {@list FusekiServerArgsCustomiser argument line customisers}.
+     * If none have been registered, return the system set of fuseki modules.
+     */
+    /*package*/ static List<FusekiServerArgsCustomiser> get() {
+        if ( customisers == null )
+            return supplierSystemCustomizers.get();
+        return customisers;
+    }
+
+    private static Supplier<List<FusekiServerArgsCustomiser>> 
supplierSystemCustomizers = ()->{
+        List<FusekiModule> x = FusekiModules.getSystemModules().asList();
+        // Generics: need an explicit cast
+        return x.stream().map(z->(FusekiServerArgsCustomiser)z).toList();
+    };
+}
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/DSGSetup.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/DSGSetup.java
index 87a11e5e45..72a684b118 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/DSGSetup.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/cmds/DSGSetup.java
@@ -20,12 +20,27 @@ package org.apache.jena.fuseki.main.cmds;
 
 import java.io.File;
 import java.nio.file.Path;
+import java.util.List;
 
+import arq.cmdline.ModDatasetAssembler;
 import org.apache.jena.atlas.io.IO;
+import org.apache.jena.atlas.lib.FileOps;
 import org.apache.jena.cmd.CmdException;
 import org.apache.jena.fuseki.system.spot.TDBOps;
+import org.apache.jena.query.Dataset;
+import org.apache.jena.riot.Lang;
+import org.apache.jena.riot.RDFDataMgr;
+import org.apache.jena.riot.RDFLanguages;
+import org.apache.jena.riot.RiotException;
+import org.apache.jena.sparql.core.DatasetGraphFactory;
+import org.apache.jena.system.Txn;
 import org.apache.jena.tdb1.TDB1Factory;
 import org.apache.jena.tdb2.DatabaseMgr;
+import org.slf4j.Logger;
+
+/**
+ * Various ways to build a dataset from command line arguments.
+ */
 
 /*package*/ class DSGSetup {
 
@@ -33,7 +48,7 @@ import org.apache.jena.tdb2.DatabaseMgr;
      * Given a path name and a preference of TDB1/TDB2 for new databases, 
return
      * details of the setup to use.
      */
-    /*package*/ static void setupTDB(String directory, boolean useTDB2, 
ServerArgs serverArgs) {
+    /*package*/ static void setupTDB(Logger log, String directory, boolean 
useTDB2, ServerArgs serverArgs) {
         File dir = Path.of(directory).toFile();
         if ( ! dir.exists() )
             throw new CmdException("Directory does not exist: " + directory);
@@ -46,40 +61,78 @@ import org.apache.jena.tdb2.DatabaseMgr;
 
         if ( IO.isEmptyDirectory(directory) ) {
             if ( useTDB2 )
-                setupTDB2(directory, serverArgs);
+                setupTDB2(log, directory, serverArgs);
             else
-                setupTDB1(directory, serverArgs);
+                setupTDB1(log, directory, serverArgs);
             return;
         }
 
         // Exists, not empty or does not exist
         if ( TDBOps.isTDB1(directory) ) {
-            setupTDB1(directory, serverArgs);
+            setupTDB1(log, directory, serverArgs);
             return;
         } else if ( TDBOps.isTDB2(directory) ) {
-            setupTDB2(directory, serverArgs);
+            setupTDB2(log, directory, serverArgs);
             return;
         } else
             throw new CmdException("Directory not a database: " + directory);
     }
 
-    private static void setupTDB1(String directory, ServerArgs serverArgs) {
+    private static void setupTDB1(Logger log, String directory, ServerArgs 
serverArgs) {
         serverArgs.datasetDescription = "TDB1 dataset: location="+directory;
         serverArgs.dsg = TDB1Factory.createDatasetGraph(directory);
     }
 
-    private static void setupTDB2(String directory, ServerArgs serverArgs) {
+    private static void setupTDB2(Logger log, String directory, ServerArgs 
serverArgs) {
         serverArgs.datasetDescription = "TDB2 dataset: location="+directory;
         serverArgs.dsg = DatabaseMgr.connectDatasetGraph(directory);
     }
 
-    public static void setupMemTDB(boolean useTDB2, ServerArgs serverArgs) {
+    /*package*/ static void setupMemTDB(Logger log, boolean useTDB2, 
ServerArgs serverArgs) {
         String tag = useTDB2 ? "TDB2" : "TDB1";
         serverArgs.datasetDescription = tag+" dataset in-memory";
         serverArgs.dsg = useTDB2
             ? DatabaseMgr.createDatasetGraph()
             : TDB1Factory.createDatasetGraph();
         serverArgs.allowUpdate = true;
+    }
+
+    /*package*/ static void setupMem(Logger log, ServerArgs serverArgs) {
+        serverArgs.datasetDescription = "in-memory";
+        serverArgs.dsg = DatasetGraphFactory.createTxnMem();
+        serverArgs.allowUpdate = true;
+    }
+
+    /*package*/ static void setupFile(Logger log, List<String> filenames, 
ServerArgs serverArgs) {
+        serverArgs.datasetDescription = "in-memory, with files loaded";
+        serverArgs.dsg = DatasetGraphFactory.createTxnMem();
+
+        for(String filename : filenames ) {
+            String pathname = filename;
+            if ( filename.startsWith("file:") )
+                pathname = filename.substring("file:".length());
+            if ( !FileOps.exists(pathname) )
+                throw new CmdException("File not found: " + filename);
+
+            // INITIAL DATA.
+            Lang language = RDFLanguages.filenameToLang(filename);
+            if ( language == null )
+                throw new CmdException("Cannot guess language for file: " + 
filename);
+            Txn.executeWrite(serverArgs.dsg,  ()-> {
+                try {
+                    log.info("Dataset: in-memory: load file: " + filename);
+                    RDFDataMgr.read(serverArgs.dsg, filename);
+                } catch (RiotException ex) {
+                    throw new CmdException("Failed to load file: " + filename);
+                }
+            });
+        }
+
+    }
 
+    public static void setupAssembler(Logger log, ModDatasetAssembler 
modDataset, ServerArgs serverArgs) {
+        serverArgs.datasetDescription = "Assembler: "+ 
modDataset.getAssemblerFile();
+        Dataset ds = modDataset.createDataset();
+        serverArgs.dsg = ds.asDatasetGraph();
     }
 }
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 181756c244..c6832a8d94 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
@@ -19,13 +19,14 @@
 package org.apache.jena.fuseki.main.cmds;
 
 import static arq.cmdline.ModAssembler.assemblerDescDecl;
+import static org.apache.jena.fuseki.main.cmds.FusekiMain.SetupType.*;
 
 import java.net.BindException;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
+import java.util.function.Consumer;
 
 import arq.cmdline.CmdARQ;
 import arq.cmdline.ModDatasetAssembler;
@@ -33,17 +34,14 @@ import 
org.apache.jena.assembler.exceptions.AssemblerException;
 import org.apache.jena.atlas.lib.FileOps;
 import org.apache.jena.atlas.logging.FmtLog;
 import org.apache.jena.atlas.web.AuthScheme;
-import org.apache.jena.cmd.ArgDecl;
-import org.apache.jena.cmd.ArgModuleGeneral;
-import org.apache.jena.cmd.CmdException;
-import org.apache.jena.cmd.TerminationException;
+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.FusekiAutoModules;
-import org.apache.jena.fuseki.main.sys.FusekiServerArgsCustomiser;
 import org.apache.jena.fuseki.main.sys.FusekiModules;
+import org.apache.jena.fuseki.main.sys.FusekiServerArgsCustomiser;
 import org.apache.jena.fuseki.server.DataAccessPoint;
 import org.apache.jena.fuseki.server.DataAccessPointRegistry;
 import org.apache.jena.fuseki.server.FusekiCoreInfo;
@@ -53,13 +51,10 @@ import org.apache.jena.fuseki.validation.IRIValidator;
 import org.apache.jena.fuseki.validation.QueryValidator;
 import org.apache.jena.fuseki.validation.UpdateValidator;
 import org.apache.jena.query.ARQ;
-import org.apache.jena.query.Dataset;
 import org.apache.jena.rdfs.RDFSFactory;
-import org.apache.jena.riot.*;
-import org.apache.jena.sparql.core.DatasetGraphFactory;
+import org.apache.jena.riot.RDFDataMgr;
+import org.apache.jena.riot.RDFParser;
 import org.apache.jena.sys.JenaSystem;
-import org.apache.jena.system.Txn;
-import org.apache.jena.tdb1.transaction.TransactionManager;
 import org.slf4j.Logger;
 
 public class FusekiMain extends CmdARQ {
@@ -121,12 +116,10 @@ public class FusekiMain extends CmdARQ {
 
     private static ArgDecl  argValidators   = new ArgDecl(ArgDecl.NoValue,  
"validators");
 
-    private static List<ArgModuleGeneral> additionalArgs = new ArrayList<>();
+    private ModDatasetAssembler modDataset  = new ModDatasetAssembler();
+    private List<FusekiServerArgsCustomiser> customiseServerArgs;
+    private final ServerArgs serverArgs     = new ServerArgs();
 
-    // private static ModLocation modLocation = new ModLocation();
-    private static ModDatasetAssembler modDataset      = new 
ModDatasetAssembler();
-
-    private final ServerArgs serverArgs  = new ServerArgs();
     // Default
     private boolean useTDB2 = true;
 
@@ -140,11 +133,11 @@ public class FusekiMain extends CmdARQ {
     public static FusekiServer.Builder builder(String... args) {
         // Parses command line, sets arguments.
         FusekiMain fusekiMain = new FusekiMain(args);
-        // Process command line args according to the argument specified.
+        // Process command line args according to the arguments specified.
         fusekiMain.process();
-        // Apply command line/serverArgs to a builder.
-        FusekiServer.Builder builder = fusekiMain.builder();
-        applyServerArgs(builder, fusekiMain.serverArgs);
+        // Apply serverArgs to a builder.
+        FusekiServer.Builder builder = FusekiServer.create();
+        fusekiMain.applyServerArgs(builder, fusekiMain.serverArgs);
         return builder;
     }
 
@@ -159,27 +152,37 @@ public class FusekiMain extends CmdARQ {
     /**
      * Create a server and run, within the same JVM.
      * This is the command line entry point.
+     * This function does not return.
+     * See also {@link #build} to create and return a server.
      */
-    static void run(String... argv) {
+    public static void run(String... argv) {
         JenaSystem.init();
         new FusekiMain(argv).mainRun();
     }
 
     /**
-     * Registers a custom arguments module
-     * <p>
-     * This approach is useful when your custom arguments affect global 
server/runtime setup and don't need to directly
-     * impact Fuseki Server building.  If you need to impact server building 
then use
-     * {@link #addCustomiser(FusekiServerArgsCustomiser)} instead
-     * </p>
-     * @param argModule Arguments module
+     * Registers a custom arguments module.
+     *
      * @deprecated Register a {@link 
org.apache.jena.fuseki.main.sys.FusekiServerArgsCustomiser} via
-     * {@link #addCustomiser(FusekiServerArgsCustomiser)} instead.
+     *             {@link #addCustomiser(FusekiServerArgsCustomiser)} instead.
      */
-    @Deprecated(forRemoval =  true)
-    public static void addArgModule(ArgModuleGeneral argModule) { 
additionalArgs.add(argModule); }
+    @Deprecated(forRemoval = true)
+    public static void addArgModule(ArgModuleGeneral argModule) {
+        FusekiServerArgsCustomiser customiser =
+        new FusekiServerArgsCustomiser() {
+            final ArgModuleGeneral argMod = argModule;
+            @Override
+            public void serverArgsModify(CmdGeneral fusekiCmd, ServerArgs 
serverArgs) {
+                fusekiCmd.addModule(argMod);
+            }
+            @Override
+            public void serverArgsPrepare(CmdGeneral fusekiCmd, ServerArgs 
serverArgs) {
+                argMod.processArgs(fusekiCmd);
+            }
+        };
+        addCustomiser(customiser);
+    }
 
-    private static final List<FusekiServerArgsCustomiser> customiseServerArgs 
= new ArrayList<>();
     /**
      * Registers a CLI customiser
      * <p>
@@ -191,29 +194,56 @@ public class FusekiMain extends CmdARQ {
      */
     public static void addCustomiser(FusekiServerArgsCustomiser customiser) {
         Objects.requireNonNull(customiser);
-        customiseServerArgs.add(customiser);
+        ArgCustomizers.addCustomiser(customiser);
     }
 
     /**
      * Resets any previously registered CLI customisers
      */
     public static void resetCustomisers() {
-        customiseServerArgs.clear();
+        ArgCustomizers.resetCustomisers();
     }
 
-    // --
+    private void applyCustomisers(Consumer<FusekiServerArgsCustomiser> action) 
{
+        for (FusekiServerArgsCustomiser customiser : customiseServerArgs) {
+            action.accept(customiser);
+        }
+    }
+//
+//    private List<FusekiServerArgsCustomiser> customiseServerArgs() {
+//        if ( customiseServerArgs == null )
+//            customiseServerArgs = new arrayList<>();
+//        return customiseServerArgs;
+//    }
+
+    // Customiser Lifecycle
+    //
+    // :: Setup args
+    // -- Called at the end of FusekiMain.argumentsSetup() after the standard 
arguments have been added.
+    // ---- FusekiServerArgsCustomiser.serverArgsModify(CmdLineGeneral, 
ServerArgs)
+    //
+    // :: Get values
+    // -- End of FusekiMain.processModulesAndArgs
+    // ---- FusekiServerArgsCustomiser.serverArgsPrepare(CmdGeneral fusekiCmd, 
ServerArgs serverArgs)
+
+    // :: Prepare server builder
+    // -- End of applyServerArgs
+    // ---- FusekiServerArgsCustomiser.serverArgsBuilder(FusekiServer.Builder 
serverBuilder, Model configModel)
+
+    // == builder.build.
+    // Decide FusekiModules
+    // --> then into FusekiBuildCycle.prepare(FusekiServer.Builder 
serverBuilder, Set<String> datasetNames, Model configModel) { }
 
     protected FusekiMain(String... argv) {
         super(argv);
+        // Snapshot for this FusekiMain instance
+        customiseServerArgs = List.copyOf(ArgCustomizers.get());
+        argumentsSetup();
+    }
 
-        if ( false )
-            // Consider ...
-            TransactionManager.QueueBatchSize = 
TransactionManager.QueueBatchSize / 2;
-
+    private void argumentsSetup() {
         getUsage().startCategory("Fuseki Main");
 
-        additionalArgs.forEach(aMod->super.addModule(aMod));
-
         // Control the order!
         add(argMem, "--mem",
             "Create an in-memory, non-persistent dataset for the server");
@@ -227,14 +257,13 @@ public class FusekiMain extends CmdARQ {
             "Use an existing TDB database (or create if does not exist)");
         add(argMemTDB, "--memTDB",
             "Create an in-memory, non-persistent dataset using TDB (testing 
only)");
-//            add(argEmpty, "--empty",
-//                "Run with no datasets and services");
         add(argRDFS, "--rdfs=FILE",
             "Apply RDFS on top of the dataset");
         add(argConfig, "--config=FILE",
             "Use a configuration file to determine the services");
         addModule(modDataset);
-        add(argEmpty); // Hidden for now.
+
+        add(argEmpty); // Hidden
         add(argPort, "--port",
             "Listen on this port number");
         add(argLocalhost, "--localhost",
@@ -269,8 +298,6 @@ public class FusekiMain extends CmdARQ {
             "jetty.xml server configuration");
         add(argCORS, "--cors=FILE", "Configure CORS settings from file");
         add(argNoCORS, "--no-cors", "Disable CORS");
-        // put in the configuration file
-//            add(argRealm, "--realm=REALM", "Realm name");
 
         add(argWithPing,    "--ping",       "Enable /$/ping");
         add(argWithStats,   "--stats",      "Enable /$/stats");
@@ -281,63 +308,116 @@ public class FusekiMain extends CmdARQ {
 
         super.modVersion.addClass("Fuseki", Fuseki.class);
 
-        for (FusekiServerArgsCustomiser customiser : customiseServerArgs) {
-            customiser.serverArgsModify(this);
-        }
+        applyCustomisers(customiser -> customiser.serverArgsModify(this, 
serverArgs));
     }
 
-    static String argUsage = "[--config=FILE] 
[--mem|--desc=AssemblerFile|--file=FILE] [--port PORT] /DatasetPathName";
+    static String argUsage = "[--config=FILE|--mem|--loc=DIR|--file=FILE] 
[--port PORT] /DatasetPathName";
 
     @Override
     protected String getSummary() {
         return getCommandName() + " " + argUsage;
     }
 
+    // Command line DSG
+    enum SetupType { UNSET,
+                   MEM, FILE, TDB, MEMTDB,  // Datasets on the command line
+                   CONF,                    // Configuration file.
+                   ASSEM,                   // Assembler for a datasets. 
Legacy.
+                   NONE,                   // Explicitly no dataset or 
configurtion file.
+                   SPARQLer                 // SPARQler mode
+    }
+
     @Override
     protected void processModulesAndArgs() {
         Logger log = Fuseki.serverLog;
-
         serverArgs.verboseLogging = super.isVerbose();
+        if ( ! serverArgs.bypassStdArgs )
+            processStdArguments(log);
+        // Have customisers process their own arguments.
+        applyCustomisers(customiser->customiser.serverArgsPrepare(this, 
serverArgs));
+    }
 
-        boolean allowEmpty = contains(argEmpty) || contains(argSparqler);
+    private void processStdArguments(Logger log) {
+        // Allow, but not require, no dataset or configuration
+        boolean allowEmpty = serverArgs.allowEmpty;
 
-        // ---- Checking consistency
+        // ---- Definition type
         int numDefinitions = 0;
+        SetupType setup = UNSET;
 
-        if ( contains(argMem) )
+        if ( contains(argMem) ) {
+            setup = MEM;
             numDefinitions++;
-        if ( contains(argFile) )
+        }
+        if ( contains(argFile) ) {
+            setup = FILE;
             numDefinitions++;
-        if ( contains(assemblerDescDecl) )
+        }
+        if ( contains(assemblerDescDecl) ) {
+            setup = ASSEM;
             numDefinitions++;
-        if ( contains(argTDB) )
+        }
+        if ( contains(argTDB) ) {
+            setup = TDB;
             numDefinitions++;
-        if ( contains(argMemTDB) )
+        }
+        if ( contains(argMemTDB) ) {
+            setup = MEMTDB;
             numDefinitions++;
-        if ( contains(argConfig) )
+        }
+        if ( contains(argConfig) ) {
+            setup = CONF;
             numDefinitions++;
+        }
+        if ( contains(argEmpty) ) {
+            setup = NONE;
+            //numDefinitions++;
+        }
+        if ( contains(argSparqler) ) {
+            setup = SPARQLer;
+            //numDefinitions++;
+        }
+
+        // ---- Validation
 
-        if ( numDefinitions == 0 && ! allowEmpty )
-            throw new CmdException("No dataset specified on the command 
line.");
+        if ( setup == UNSET && allowEmpty )
+            setup = NONE;
+
+        // Starting empty.
+        boolean startEmpty = ( setup == NONE || setup == SPARQLer );
 
         if ( numDefinitions > 1 )
             throw new CmdException("Multiple ways providing a dataset. Only 
one of --mem, --file, --loc or --conf");
 
-        if ( numDefinitions > 0 && allowEmpty )
+        if ( startEmpty && numDefinitions > 0 )
             throw new CmdException("Dataset provided but 'no dataset' flag 
given");
 
-        //---- check: Invalid: --conf + service name.
+        if ( startEmpty && ! getPositional().isEmpty() )
+            throw new CmdException("Dataset name provided but 'no dataset' 
flag given");
+
+        if ( ! startEmpty && numDefinitions == 0 )
+            throw new CmdException("No dataset or configuration specified on 
the command line");
+
+        // Configuration file OR dataset
+
         if ( contains(argConfig) ) {
-            if ( getPositional().size() != 0 )
+            // Invalid combination: --conf + service name.
+            if ( ! getPositional().isEmpty() )
                 throw new CmdException("Can't have both a configuration file 
and a service name");
             if ( contains(argRDFS) )
-                throw new CmdException("Need to define RDFS setup in the 
configuration file.");
+                throw new CmdException("Need to define RDFS setup in the 
configuration file");
         } else {
-            if ( ! allowEmpty && getPositional().size() == 0 )
-                throw new CmdException("Missing service name");
+            // No --conf
             if ( getPositional().size() > 1 )
                 throw new CmdException("Multiple dataset path names given");
-            if ( getPositional().size() != 0 )
+            if ( ! startEmpty && getPositional().size() == 0 ) {
+                if ( setup == UNSET )
+                    throw new CmdException("Missing dataset description and 
service name");
+                else
+                    throw new CmdException("Missing service name");
+            }
+            // Finally!
+            if ( getPositional().size() == 1 )
                 serverArgs.datasetPath = 
DataAccessPoint.canonical(getPositionalArg(0));
         }
 
@@ -349,24 +429,9 @@ public class FusekiMain extends CmdARQ {
         boolean allowUpdate = contains(argUpdate);
         serverArgs.allowUpdate = allowUpdate;
 
-        boolean hasJettyConfigFile = contains(argJettyConfig);
-
-        // ---- Port
-        serverArgs.port = defaultPort;
-
-        if ( contains(argPort) ) {
-            if ( hasJettyConfigFile )
-                throw new CmdException("Cannot specify the port and also 
provide a Jetty configuration file");
-            serverArgs.port = portNumber(argPort);
-        }
-
-        if ( contains(argLocalhost) ) {
-            if ( hasJettyConfigFile )
-                throw new CmdException("Cannot specify 'localhost' and also 
provide a Jetty configuration file");
-            serverArgs.loopback = true;
-        }
-
         // ---- Dataset
+        // A server has one of the command line dataset setups or a 
configuration file,
+        // or "--empty" or "--sparqler"
         // Only one of these is chosen from the checking above.
 
         // Which TDB to use to create a command line TDB database.
@@ -375,70 +440,47 @@ public class FusekiMain extends CmdARQ {
         if ( contains(argTDB2mode) )
             useTDB2 = true;
 
-        if ( allowEmpty ) {
-            serverArgs.empty = true;
-            serverArgs.datasetDescription = "No dataset";
-        }
-
-        // Fuseki config file
-        if ( contains(argConfig) )
-            serverArgs.serverConfigFile = getValue(argConfig);
-
-        // Ways to set up a dataset.
-        if ( contains(argMem) ) {
-            serverArgs.datasetDescription = "in-memory";
-            // Only one setup should be called by the test above but to be safe
-            // and in case of future changes, clear the configuration.
-            serverArgs.dsg = DatasetGraphFactory.createTxnMem();
-            // Always allow, else you can't do very much!
-            serverArgs.allowUpdate = true;
-        }
-
-        if ( contains(argFile) ) {
-            List<String> filenames = getValues(argFile);
-            serverArgs.datasetDescription = "in-memory, with files loaded";
-            // Update is not enabled by default for --file
-            serverArgs.allowUpdate = contains(argUpdate);
-            serverArgs.dsg = DatasetGraphFactory.createTxnMem();
-
-            for(String filename : filenames ) {
-                String pathname = filename;
-                if ( filename.startsWith("file:") )
-                    pathname = filename.substring("file:".length());
-                if ( !FileOps.exists(pathname) )
-                    throw new CmdException("File not found: " + filename);
-
-                // INITIAL DATA.
-                Lang language = RDFLanguages.filenameToLang(filename);
-                if ( language == null )
-                    throw new CmdException("Cannot guess language for file: " 
+ filename);
-                Txn.executeWrite(serverArgs.dsg,  ()-> {
-                    try {
-                        log.info("Dataset: in-memory: load file: " + filename);
-                        RDFDataMgr.read(serverArgs.dsg, filename);
-                    } catch (RiotException ex) {
-                        throw new CmdException("Failed to load file: " + 
filename);
-                    }
-                });
+        switch(setup) {
+            case CONF->{
+                serverArgs.serverConfigFile = getValue(argConfig);
             }
+            case MEM->{
+                serverArgs.dsgMaker = args->DSGSetup.setupMem(log, args);
+            }
+            case FILE->{
+                List<String> filenames = getValues(argFile);
+                serverArgs.dsgMaker = args->DSGSetup.setupFile(log, filenames, 
args);
+            }
+            case TDB->{
+                String directory = getValue(argTDB);
+                serverArgs.dsgMaker = args->DSGSetup.setupTDB(log, directory, 
useTDB2, args);
+            }
+            case NONE->{
+                serverArgs.startEmpty = true;
+                serverArgs.datasetDescription = "No dataset";
+            }
+            case ASSEM->{
+                serverArgs.dsgMaker = args->DSGSetup.setupAssembler(log, 
modDataset, args);
+            }
+            case MEMTDB->{
+                DSGSetup.setupMemTDB(log, useTDB2, serverArgs);
+            }
+            case UNSET->{
+                throw new CmdException("Internal error");
+            }
+            case SPARQLer -> {
+                String filebase = getValue(argSparqler);
+                if ( !FileOps.exists(filebase) )
+                    throw new CmdException("File area not found: " + filebase);
+                serverArgs.contentDirectory = filebase;
+                serverArgs.addGeneral = "/sparql";
+                serverArgs.startEmpty = true;
+                serverArgs.validators = true;
+            }
+            default -> throw new IllegalArgumentException("Unexpected value: " 
+ setup);
         }
 
-        if ( contains(argMemTDB) ) {
-            DSGSetup.setupMemTDB(useTDB2, serverArgs);
-        }
-
-        if ( contains(argTDB) ) {
-            String directory = getValue(argTDB);
-            DSGSetup.setupTDB(directory, useTDB2, serverArgs);
-        }
-
-        if ( contains(assemblerDescDecl) ) {
-            serverArgs.datasetDescription = "Assembler: "+ 
getValue(assemblerDescDecl);
-            // Need to add service details.
-            Dataset ds = modDataset.createDataset();
-            serverArgs.dsg = ds.asDatasetGraph();
-        }
-
+        // ---- RDFS
         if ( contains(argRDFS) ) {
             String rdfsVocab = getValue(argRDFS);
             if ( !FileOps.exists(rdfsVocab) )
@@ -454,16 +496,6 @@ public class FusekiMain extends CmdARQ {
             ARQ.getContext().set(ARQ.queryTimeout, str);
         }
 
-        if ( contains(argSparqler) ) {
-            String filebase = getValue(argSparqler);
-            if ( ! FileOps.exists(filebase) )
-                throw new CmdException("File area not found: "+filebase);
-            serverArgs.contentDirectory = filebase;
-            serverArgs.addGeneral = "/sparql";
-            serverArgs.empty = true;
-            serverArgs.validators = true;
-        }
-
         if ( contains(argGeneralQuerySvc) ) {
             String z = getValue(argGeneralQuerySvc);
             if ( ! z.startsWith("/") )
@@ -477,6 +509,23 @@ public class FusekiMain extends CmdARQ {
 
         // -- Server setup.
 
+        boolean hasJettyConfigFile = contains(argJettyConfig);
+
+        // ---- Port
+        serverArgs.port = defaultPort;
+
+        if ( contains(argPort) ) {
+            if ( hasJettyConfigFile )
+                throw new CmdException("Cannot specify the port and also 
provide a Jetty configuration file");
+            serverArgs.port = portNumber(argPort);
+        }
+
+        if ( contains(argLocalhost) ) {
+            if ( hasJettyConfigFile )
+                throw new CmdException("Cannot specify 'localhost' and also 
provide a Jetty configuration file");
+            serverArgs.loopback = true;
+        }
+
         if ( contains(argContextPath) ) {
             String contextPath = getValue(argContextPath);
             contextPath = sanitizeContextPath(contextPath);
@@ -539,7 +588,7 @@ public class FusekiMain extends CmdARQ {
             // Allows for external setting of serverArgs.fusekiModules
             if ( serverArgs.fusekiModules == null ) {
                 FusekiAutoModules.setup();
-                serverArgs.fusekiModules = FusekiAutoModules.load();
+                serverArgs.fusekiModules = FusekiModules.getSystemModules();
             }
         } else {
             // Disabled module discovery.
@@ -563,11 +612,6 @@ public class FusekiMain extends CmdARQ {
         serverArgs.withStats = contains(argWithStats);
         serverArgs.withMetrics = contains(argWithMetrics);
         serverArgs.withCompact = contains(argWithCompact);
-
-        // Deal with any customisers
-        for (FusekiServerArgsCustomiser customiser : customiseServerArgs) {
-            customiser.serverArgsPrepare(this, serverArgs);
-        }
     }
 
     private int portNumber(ArgDecl arg) {
@@ -598,6 +642,7 @@ public class FusekiMain extends CmdARQ {
 
     @Override
     protected void exec() {
+        // Arguments have been processed to set serverArgs
         try {
             Logger log = Fuseki.serverLog;
             FusekiMainInfo.logServerCode(log);
@@ -635,25 +680,21 @@ public class FusekiMain extends CmdARQ {
      * The server has not been started.
      */
     private FusekiServer makeServer(ServerArgs serverArgs) {
-        FusekiServer.Builder builder = builder();
+        FusekiServer.Builder builder = FusekiServer.create();
         return buildServer(builder, serverArgs);
     }
 
-    protected FusekiServer.Builder builder() {
-        return FusekiServer.create();
-    }
-
     /**
      * Process {@link ServerArgs} and build a server.
      * The server has not been started.
      */
-    private static FusekiServer buildServer(FusekiServer.Builder builder, 
ServerArgs serverArgs) {
+    private FusekiServer buildServer(FusekiServer.Builder builder, ServerArgs 
serverArgs) {
         applyServerArgs(builder, serverArgs);
         return builder.build();
     }
 
     /** Apply {@link ServerArgs} to a {@link FusekiServer.Builder}. */
-    private static void applyServerArgs(FusekiServer.Builder builder, 
ServerArgs serverArgs) {
+    private void applyServerArgs(FusekiServer.Builder builder, ServerArgs 
serverArgs) {
         if ( serverArgs.jettyConfigFile != null )
             builder.jettyServerConfig(serverArgs.jettyConfigFile);
         builder.port(serverArgs.port);
@@ -672,23 +713,15 @@ public class FusekiMain extends CmdARQ {
             builder.addServlet("/$/validate/data",   new DataValidator());
         }
 
-//        if ( ! serverArgs.empty ) {
-//            if ( serverArgs.serverConfig != null )
-//                // Config file.
-//                builder.parseConfigFile(serverArgs.serverConfig);
-//            else
-//                // One dataset.
-//                builder.add(serverArgs.datasetPath, serverArgs.dsg, 
serverArgs.allowUpdate);
-//        }
-
-        if ( ! serverArgs.empty ) {
-            // A CLI customiser may have already set the model.
+        // Apply argument for the database services
+        if ( ! serverArgs.startEmpty ) {
             if (serverArgs.serverConfigModel != null ) {
+                // -- Customiser has already set the configuration model
                 builder.parseConfig(serverArgs.serverConfigModel);
                 serverArgs.datasetDescription = "Configuration: provided";
             } else if ( serverArgs.serverConfigFile != null ) {
+                // -- Configuration file.
                 // if there is a configuration file, read it.
-                // Read the model.
                 String file = serverArgs.serverConfigFile;
                 if ( file.startsWith("file:") )
                     file = file.substring("file:".length());
@@ -699,13 +732,20 @@ public class FusekiMain extends CmdARQ {
                     throw new CmdException("Is a directory: "+file);
                 serverArgs.datasetDescription = "Configuration: 
"+path.toAbsolutePath();
                 serverArgs.serverConfigModel = 
RDFParser.source(path).toModel();
+                // ... and perform server configuration
                 builder.parseConfig(serverArgs.serverConfigModel);
             } else {
-                // One dataset up by command line arguments.
-                if ( serverArgs.dsg == null || serverArgs.datasetPath == null 
) {
+                // -- A dataset setup by command line arguments.
+                if ( serverArgs.datasetPath == null )
+                    throw new CmdException("No URL path name for the dataset");
+                // The dataset setup by command line arguments.
+                // A customizer may have set the dataset.
+                if ( serverArgs.dsg == null )
+                    serverArgs.dsgMaker.accept(serverArgs);
+                // Should have been set somehow by this point.
+                if ( serverArgs.dsg == null )
                     // Internal error: should have happened during checking 
earlier.
                     throw new CmdException("Failed to set the dataset 
service");
-                }
                 builder.add(serverArgs.datasetPath, serverArgs.dsg, 
serverArgs.allowUpdate);
             }
         }
@@ -746,10 +786,8 @@ public class FusekiMain extends CmdARQ {
         if ( serverArgs.withCompact )
             builder.enableCompact(true);
 
-        // Allow customisers to process their own arguments.
-        for (FusekiServerArgsCustomiser customiser : customiseServerArgs) {
-            customiser.serverArgsBuilder(builder, builder.configModel());
-        }
+        // Allow customisers to inspect and modify the builder.
+        applyCustomisers(customiser->customiser.serverArgsBuilder(builder, 
serverArgs.serverConfigModel));
     }
 
     /** Information from the command line setup */
@@ -757,11 +795,11 @@ public class FusekiMain extends CmdARQ {
         if ( super.isQuiet() )
             return;
 
-        if ( serverArgs.empty ) {
-            FmtLog.info(log, "No SPARQL datasets services");
+        if ( serverArgs.startEmpty ) {
+            FmtLog.info(log, "No SPARQL dataset services");
         } else {
             if ( serverArgs.datasetPath == null && 
serverArgs.serverConfigModel == null )
-                log.error("No dataset path nor server configuration file");
+                log.error("No dataset path or server configuration file");
         }
 
         DataAccessPointRegistry dapRegistry = 
DataAccessPointRegistry.get(server.getServletContext());
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 62dc3f3261..930147ca36 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
@@ -18,6 +18,8 @@
 
 package org.apache.jena.fuseki.main.cmds;
 
+import java.util.function.Consumer;
+
 import org.apache.jena.atlas.web.AuthScheme;
 import org.apache.jena.fuseki.main.sys.FusekiModules;
 import org.apache.jena.graph.Graph;
@@ -31,56 +33,66 @@ import org.apache.jena.sparql.core.DatasetGraph;
  */
 public class ServerArgs {
     /** Server port. This is the http port when both http and https are 
active. */
-    public int port                     = -1;
+    public int port                       = -1;
     /** Loopback */
-    public boolean   loopback           = false;
+    public boolean loopback               = false;
 
     // https
-    public int httpsPort                = -1;
-    public String httpsKeysDetails      = null;
+    public int httpsPort                  = -1;
+    public String httpsKeysDetails        = null;
 
     // Jetty server configuration file.
-    public String jettyConfigFile       = null;
+    public String jettyConfigFile         = null;
 
     /** The dataset name (canonical form) */
-    public String    datasetPath        = null;
+    public String  datasetPath            = null;
     /** Allow update */
-    public boolean   allowUpdate        = false;
+    public boolean allowUpdate            = false;
 
-    public boolean   verboseLogging     = false;
+    public boolean verboseLogging         = false;
 
-    // FusekiModules to use for this server build.
-    public FusekiModules fusekiModules  = null;
+    /**
+     * FusekiModules to use during the server build *
+     * Command line customisers are handled separately by FusekiMain.
+     */
+    public FusekiModules fusekiModules    = null;
 
-    public boolean withCORS             = true;
-    public String corsConfigFile        = null;
-    public boolean withPing             = false;
-    public boolean withStats            = false;
-    public boolean withMetrics          = false;
-    public boolean withCompact          = false;
+    public boolean withCORS               = true;
+    public String corsConfigFile          = null;
+    public boolean withPing               = false;
+    public boolean withStats              = false;
+    public boolean withMetrics            = false;
+    public boolean withCompact            = false;
 
-    // This is set ...
-    public DatasetGraph dsg             = null;
+    // Either a dataset setup from the command line (delayed creation of the 
dataset) ...
+    public Consumer<ServerArgs> dsgMaker  = null;
+    public DatasetGraph dsg               = null;
     /** RDFS dataset - only when dataset is defined on the command line. */
-    public Graph rdfsGraph              = null;
+    public Graph rdfsGraph                = null;
+
+    // ... or configuration file.
+    public String serverConfigFile        = null;
+    public Model serverConfigModel        = null;
 
-    // ... or this.
-    public String serverConfigFile      = null;
-    public Model serverConfigModel      = null;
+    /** Allow no datasets without it being an error. This is not an argument. 
*/
+    public boolean allowEmpty             = false;
+    /** Start without a dataset or configuration (this is {@code --empty}) */
+    public boolean startEmpty             = false;
 
-    /** No registered datasets without it being an error. */
-    public boolean empty                = false;
     /** General query processor servlet */
-    public String addGeneral            = null;
+    public String addGeneral              = null;
 
-    public boolean validators           = false;
+    public boolean validators             = false;
     /** An informative label */
-    public String datasetDescription    = null;
-    public String servletContextPath    = null;
-    public String contentDirectory      = null;
+    public String datasetDescription      = null;
+    public String servletContextPath      = null;
+    public String contentDirectory        = null;
 
     // Server authentication
-    public AuthScheme authScheme        = null;
-    public String passwdFile            = null;
-    public String realm                 = null;
+    public AuthScheme authScheme          = null;
+    public String passwdFile              = null;
+    public String realm                   = null;
+
+    /** Don't process standard arguments. This is a not a command argument.*/
+    public boolean bypassStdArgs          = false;
 }
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiAutoModules.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiAutoModules.java
index 80e5a368e0..59b57e6465 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiAutoModules.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiAutoModules.java
@@ -32,7 +32,7 @@ import org.apache.jena.fuseki.main.FusekiServer;
 import org.slf4j.Logger;
 
 /**
- * Control of {@link FusekiAutoModule} found via {@link ServiceLoader}.
+ * Management of {@link FusekiAutoModule automatically loaded modules} found 
via {@link ServiceLoader}.
  */
 public class FusekiAutoModules {
 
@@ -44,7 +44,6 @@ public class FusekiAutoModules {
 
     private static boolean allowDiscovery = true;
     private static boolean enabled = true;
-    private static FusekiModules altFusekiModules = null;
 
     /*package*/ static boolean logModuleLoading() {
         return Lib.isPropertyOrEnvVarSetToTrue(logLoadingProperty, 
envLogLoadingProperty);
@@ -78,37 +77,40 @@ public class FusekiAutoModules {
         autoModules = createServiceLoaderModules();
     }
 
+    private static FusekiModules currentLoadedModules = null;
+
     /**
-     * Load the the system wide Fuseki modules if it has not already been 
loaded.
-     * If disabled, return an empty  FusekiModules
+     * Load FusekiAutoModules. This call reloads the modules every call.
+     * If disabled, return an empty {@link FusekiModules}.
      */
-    public static FusekiModules load() {
+    static FusekiModules load() {
         if ( ! enabled )
             return FusekiModules.empty();
-        if ( altFusekiModules != null )
-            return altFusekiModules;
-        return getServiceLoaderModules().load();
+        currentLoadedModules = getServiceLoaderModules().load();
+        return get();
+    }
+
+    /**
+     * Return the current (last loaded) Fuseki auto-modules.
+     */
+    static FusekiModules get() {
+        if ( currentLoadedModules == null )
+            load();
+        return currentLoadedModules;
     }
 
     // -- ServiceLoader machinery.
 
     // testing
     /*package*/ static void reset() {
-        autoModules = null;
-    }
-
-    /**
-     * Ignore any discovered modules and use the given FusekiModules.
-     * Pass null to clear a previous setting.
-     */
-    static void setSystemDefault(FusekiModules fusekiModules) {
-        altFusekiModules = fusekiModules;
+        load();
     }
 
     // Single auto-module controller.
     private static FusekiServiceLoaderModules autoModules = null;
 
     private static FusekiServiceLoaderModules getServiceLoaderModules() {
+        // Load once.
         if ( autoModules == null )
             setup();
         return autoModules;
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiModules.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiModules.java
index 6dec6b02dc..a0af6600d7 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiModules.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiModules.java
@@ -23,16 +23,51 @@ import java.util.Objects;
 import java.util.function.Consumer;
 
 /**
- * List of {@linkplain FusekiModule Fuseki modules}.
- * This is the immutable group of modules for a server.
+ * A collection of {@linkplain FusekiModule Fuseki modules}.
  * <p>
+ * There is one specific collection of modules - a system wide set of modules.
+ * This collection defaults to the automatically discovered modules {@link 
FusekiAutoModules#load()}.
+ *
  * @see FusekiAutoModules
  */
 public class FusekiModules {
 
+    private static FusekiModules autoLoadedFusekiModules = 
FusekiAutoModules.get();
+    // Never null, maybe empty
+    private static FusekiModules systemFusekiModules = autoLoadedFusekiModules;
+
+    /**
+     * There is a system wide set of modules used when o other are indicated.
+     * These default to the automatically discovered modules.
+     */
+    public static void setSystemDefault(FusekiModules fusekiModules) {
+        systemFusekiModules = ( fusekiModules == null ) ? 
FusekiModules.create() : fusekiModules;
+    }
+
+    /** Restore the original setting of the system default collection. */
+    public static void restoreSystemDefault() {
+        systemFusekiModules = autoLoadedFusekiModules;
+    }
+
+    public static FusekiModules getSystemModules() {
+        return systemFusekiModules;
+    }
+
+    // Do initialization at a predictable point.
+    /*package*/ static void init() {}
+
+    // ----
+
     /** A Fuseki module with no members. */
     public static final FusekiModules empty() { return FusekiModules.create(); 
}
 
+    // Testing.
+    /*package*/ static void resetSystemDefault() {
+        // Reload, reset. Fresh objects.
+        autoLoadedFusekiModules = FusekiAutoModules.load();
+        systemFusekiModules = autoLoadedFusekiModules;
+    }
+
     /** Create a collection of Fuseki modules */
     public static FusekiModules create(FusekiModule ... modules) {
         return new FusekiModules(modules);
@@ -43,6 +78,10 @@ public class FusekiModules {
         return new FusekiModules(modules);
     }
 
+//    public static FusekiModules autoloadedModules() {
+//        return FusekiAutoModules.load();
+//    }
+
     private final List<FusekiModule> modules;
 
     private FusekiModules(FusekiModule ... modules) {
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 a43ff2d972..e3abd58165 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
@@ -76,8 +76,9 @@ public interface FusekiServerArgsCustomiser {
      * The server construction is aborted.
      *
      * @param fusekiCmd Fuseki Main command line arguments
+     * @param serverArgs Intial setting before command line processing.
      */
-    public default void serverArgsModify(CmdGeneral fusekiCmd) { }
+    public default void serverArgsModify(CmdGeneral fusekiCmd, ServerArgs 
serverArgs) { }
 
     /**
      * Called at the end command line argument processing.
@@ -85,6 +86,9 @@ public interface FusekiServerArgsCustomiser {
      * This allows a Fuseki module to pull out custom arguments it has added 
and
      * process them appropriately, including validating or modifying the
      * {@link ServerArgs} that will be used to build the server.
+     * This method can set the set the dataset, in which case
+     * a command line dataset setup or configuration file server
+     * set up is not performed.
      * <p>
      * This method can throw {@link CmdException} to indicate errors.
      * This will cause a error message to be printed, without the stack trace.
@@ -99,8 +103,11 @@ public interface FusekiServerArgsCustomiser {
     /**
      * Called at the end of applying the {@link ServerArgs} to the builder.
      * <p>
-     * The configuration model (if any) has been setup. This step can do 
validation
-     * and argument processing dependent on the configuration model.
+     * This step can do validation and argument processing dependent on the 
configuration model.
+     * <p>
+     * If there is a configuration model, this has been processed by the 
builder
+     * <p>
+     * If a command line dataset setup is being used, this is the dataset has 
been created.
      * <p>
      * This method can throw {@link CmdException} to indicate errors.
      * This will cause a error message to be printed, without the stack trace.
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/InitFuseki.java
 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/InitFuseki.java
index 0b0daafe87..ef6259d065 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/InitFuseki.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/InitFuseki.java
@@ -41,10 +41,6 @@ public class InitFuseki implements JenaSubsystemLifecycle {
     @Override
     public int level() { return 101; }
 
-    /**
-     * Load modules, call "start()".
-     * Each {@link FusekiModule} will be called during the server build 
process.
-     */
     public static void init() {
         if ( initialized ) {
             return;
@@ -56,8 +52,8 @@ public class InitFuseki implements JenaSubsystemLifecycle {
             }
             initialized = true;
             JenaSystem.logLifecycle("Fuseki.init - start");
-            // Leave until known to be needed FusekiServer build with no 
specific FusekikModules given.
-            // FusekiModulesCtl.setup();
+
+            FusekiModules.init();
             try {
                 Cmds.injectCmd("fuseki", a->FusekiMainCmd.main(a));
             } catch (NoClassDefFoundError ex) {
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiMainCmdArguments.java
 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiMainCmdArguments.java
index 9992139d88..8a22316519 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiMainCmdArguments.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiMainCmdArguments.java
@@ -18,10 +18,7 @@
 package org.apache.jena.fuseki.main;
 
 import static java.util.Collections.emptyList;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.*;
 
 import java.io.File;
 import java.io.IOException;
@@ -33,8 +30,10 @@ import org.apache.jena.atlas.web.WebLib;
 import org.apache.jena.cmd.CmdException;
 import org.apache.jena.fuseki.Fuseki;
 import org.apache.jena.fuseki.main.cmds.FusekiMain;
+import org.apache.jena.fuseki.main.cmds.ServerArgs;
 import org.apache.jena.fuseki.system.FusekiLogging;
 import org.apache.jena.riot.SysRIOT;
+
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
@@ -55,7 +54,7 @@ public class TestFusekiMainCmdArguments {
         level = LogCtl.getLevel(Fuseki.serverLog);
         LogCtl.setLevel(Fuseki.serverLog, "WARN");
 
-        // Create a fake Jetty config file just to avoid File Not Found error
+        // Create a fake Jetty config file just to avoid "File Not Found" error
         jettyConfigFile = Files.createTempFile("jetty-config",".xml").toFile();
         jettyConfigFilename = jettyConfigFile.getAbsolutePath();
     }
@@ -72,10 +71,19 @@ public class TestFusekiMainCmdArguments {
             server.stop();
     }
 
+    // Test the initial settings in ServerArgs for customisation
+    // control features that are not argument values.
+    @Test
+    public void argDefaults() {
+        ServerArgs serverArgs = new ServerArgs();
+        assertFalse("Wrong default setting: allowEmpty", 
serverArgs.allowEmpty);
+        assertFalse("Wrong default setting: bypassStdArgs", 
serverArgs.bypassStdArgs);
+    }
+
     @Test
     public void test_empty() {
         // given
-        List<String> arguments = List.of("--port=0", "--empty", "/dataset");
+        List<String> arguments = List.of("--port=0", "--empty");
         // when
         buildServer(buildCmdLineArguments(arguments));
         // then
@@ -124,6 +132,15 @@ public class TestFusekiMainCmdArguments {
         assertNotNull(server);
     }
 
+    @Test
+    public void test_empty_but_named() {
+        // given
+        List<String> arguments = List.of("--port=0", "--empty", "/dataset");
+        String expectedMessage = "Dataset name provided but 'no dataset' flag 
given";
+        // when, then
+        testForCmdException(arguments, expectedMessage);
+    }
+
     @Test
     public void test_error_contextpath() {
         // given
@@ -137,7 +154,7 @@ public class TestFusekiMainCmdArguments {
     public void test_error_noDataSetProvided() {
         // given
         List<String> arguments = List.of("/dataset");
-        String expectedMessage = "No dataset specified on the command line.";
+        String expectedMessage = "No dataset or configuration specified on the 
command line";
         // when, then
         testForCmdException(arguments, expectedMessage);
     }
@@ -146,7 +163,7 @@ public class TestFusekiMainCmdArguments {
     public void test_error_noArguments_emptyString() {
         // given
         String emptyString = "";
-        String expectedMessage = "No dataset specified on the command line.";
+        String expectedMessage = "No dataset or configuration specified on the 
command line";
         // when
         Throwable actual = null;
         try {
@@ -164,7 +181,7 @@ public class TestFusekiMainCmdArguments {
     public void test_error_noArguments_null() {
         // given
         String nullString = null;
-        String expectedMessage = "No dataset specified on the command line.";
+        String expectedMessage = "No dataset or configuration specified on the 
command line";
         // when
         Throwable actual = null;
         try {
@@ -182,7 +199,7 @@ public class TestFusekiMainCmdArguments {
     public void test_error_noArguments_emptyList() {
         // given
         List<String> arguments = emptyList();
-        String expectedMessage = "No dataset specified on the command line.";
+        String expectedMessage = "No dataset or configuration specified on the 
command line";
         // when, then
         testForCmdException(arguments, expectedMessage);
     }
@@ -219,7 +236,7 @@ public class TestFusekiMainCmdArguments {
     public void test_error_configFileWithRDFS() {
         // given
         List<String> arguments = List.of("--config=file", "--rdfs=file");
-        String expectedMessage = "Need to define RDFS setup in the 
configuration file.";
+        String expectedMessage = "Need to define RDFS setup in the 
configuration file";
         // when, then
         testForCmdException(arguments, expectedMessage);
     }
@@ -339,7 +356,7 @@ public class TestFusekiMainCmdArguments {
     @Test
     public void test_error_missingSparqlerFile() {
         // given
-        List<String> arguments = List.of("--sparqler=missing_file", 
"/dataset");
+        List<String> arguments = List.of("--sparqler=missing_file");
         String expectedMessage = "File area not found: missing_file";
         // when, then
         testForCmdException(arguments, expectedMessage);
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiMainCmdCustomArguments.java
 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiMainCmdCustomArguments.java
index cae8c05e03..2527fee741 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiMainCmdCustomArguments.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiMainCmdCustomArguments.java
@@ -19,9 +19,7 @@
 package org.apache.jena.fuseki.main;
 
 import static org.junit.jupiter.api.Assertions.*;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
 
-import java.util.Objects;
 import java.util.function.Consumer;
 
 import org.junit.After;
@@ -93,6 +91,16 @@ public class TestFusekiMainCmdCustomArguments {
         FusekiServer server = FusekiMain.build(arguments);
     }
 
+    @Test
+    public void test_custom_allowNoArgs() {
+        String[] arguments1 = {"--port=0"};
+        FusekiServerArgsCustomiser customiser = new TestArgsAllowNoSetup();
+        FusekiServer server1 = test(customiser, arguments1, serv->{});
+        // Dataset arguments are allowed.
+        String[] arguments2 = {"--port=0","--mem", "/ds"};
+        FusekiServer server2 = test(customiser, arguments2, serv->{});
+    }
+
     @Test
     public void test_custom_flag() {
         String[] arguments = {"--port=0", "--mem", "--custom-flag", "/ds"};
@@ -108,7 +116,7 @@ public class TestFusekiMainCmdCustomArguments {
     @Test
     public void test_custom_confModel_noFlag() {
         String[] arguments = {"--port=0", "--mem", "/ds"};
-        FusekiCliCustomModel customiser = new FusekiCliCustomModel(new 
ArgDecl(false, "fixed"), confFixed);
+        TestArgsCustomModelAltArg customiser = new 
TestArgsCustomModelAltArg(new ArgDecl(false, "fixed"), confFixed);
         test(customiser, arguments, server->{
             assertSame(null, customiser.notedServerConfigModel);
             DataAccessPoint dap1 = 
server.getDataAccessPointRegistry().get("/ds");
@@ -119,10 +127,25 @@ public class TestFusekiMainCmdCustomArguments {
     }
 
     @Test
-    public void test_custom_confModel_withFlag() {
+    public void test_custom_confModel_replace() {
         // Ignores command line.
-        String[] arguments = {"--port=0", "--mem", "--fixed", "/ds"};
-        FusekiCliCustomModel customiser = new FusekiCliCustomModel(new 
ArgDecl(false, "fixed"), confFixed);
+        String[] arguments = {"--port=0", "--conf=somefile.ttl", "--fixed"};
+        FusekiServerArgsCustomiser customiser = new 
TestArgsCustomModelAltArg(new ArgDecl(false, "fixed"), confFixed);
+        FusekiServer server = test(customiser, arguments, serv->{
+            DataAccessPoint dap1 = 
serv.getDataAccessPointRegistry().get("/ds");
+            assertNull(dap1);
+            DataAccessPoint dap2 = 
serv.getDataAccessPointRegistry().get("/dataset");
+            assertNotNull(dap2);
+        });
+        
assertTrue(server.getDataAccessPointRegistry().isRegistered("/dataset"));
+        assertFalse(server.getDataAccessPointRegistry().isRegistered("/ds"));
+    }
+
+    @Test
+    public void test_custom_confModel_different() {
+        // Ignores command line --conf setting.
+        String[] arguments = {"--port=0", "--conf=somefile.ttl"};
+        FusekiServerArgsCustomiser customiser = new TestArgsMyConfModel();
         FusekiServer server = test(customiser, arguments, serv->{
             DataAccessPoint dap1 = 
serv.getDataAccessPointRegistry().get("/ds");
             assertNull(dap1);
@@ -133,10 +156,25 @@ public class TestFusekiMainCmdCustomArguments {
         assertFalse(server.getDataAccessPointRegistry().isRegistered("/ds"));
     }
 
+    @Test
+    public void test_custom_confModel_different_ignore() {
+        // --conf not used. Does not reset.
+        String[] arguments = {"--port=0", "--mem", "/ds"};
+        FusekiServerArgsCustomiser customiser = new TestArgsMyConfModel();
+        FusekiServer server = test(customiser, arguments, serv->{
+            DataAccessPoint dap1 = 
serv.getDataAccessPointRegistry().get("/ds");
+            assertNotNull(dap1);
+            DataAccessPoint dap2 = 
serv.getDataAccessPointRegistry().get("/dataset");
+            assertNull(dap2);
+        });
+        
assertFalse(server.getDataAccessPointRegistry().isRegistered("/dataset"));
+        assertTrue(server.getDataAccessPointRegistry().isRegistered("/ds"));
+    }
+
     // ----
 
     private void test(ArgDecl argDecl, String[] arguments, boolean seen, 
String value) {
-        FusekiCliCustomArg customiser = new FusekiCliCustomArg(argDecl);
+        TestArgsCustomArg customiser = new TestArgsCustomArg(argDecl);
         test(customiser, arguments, (server)->{
             assertEquals(seen, customiser.argSeen);
             assertEquals(value, customiser.argValue);
@@ -145,26 +183,36 @@ public class TestFusekiMainCmdCustomArguments {
 
     private FusekiServer test(FusekiServerArgsCustomiser customiser, String[] 
arguments, Consumer<FusekiServer> checker) {
         FusekiMain.resetCustomisers();
-        FusekiMain.addCustomiser(customiser);
+        if ( customiser != null )
+            FusekiMain.addCustomiser(customiser);
         FusekiServer server = FusekiMain.build(arguments);
         if ( checker != null )
             checker.accept(server);
         return server;
     }
 
-    // ---- Test FusekiCliCustomisers
+    // ---- Arg customisers.
+
+    // Allow no daatset or configuration.
+    static class TestArgsAllowNoSetup implements FusekiServerArgsCustomiser {
+        @Override
+        public void serverArgsModify(CmdGeneral fusekiCmd, ServerArgs 
serverArgs) {
+            serverArgs.allowEmpty = true;
+        }
+    }
 
-    static class FusekiCliCustomArg implements FusekiServerArgsCustomiser {
+    // Custom argument.
+    static class TestArgsCustomArg implements FusekiServerArgsCustomiser {
         final ArgDecl argDecl;
         String argValue = null;
         boolean argSeen = false;
 
-        FusekiCliCustomArg(ArgDecl argDecl) {
+        TestArgsCustomArg(ArgDecl argDecl) {
             this.argDecl = argDecl;
         }
 
         @Override
-        public void serverArgsModify(CmdGeneral fusekiCmd) {
+        public void serverArgsModify(CmdGeneral fusekiCmd, ServerArgs 
serverConfig) {
             fusekiCmd.add(argDecl);
         }
 
@@ -175,19 +223,20 @@ public class TestFusekiMainCmdCustomArguments {
         }
     };
 
-    static class FusekiCliCustomModel implements FusekiServerArgsCustomiser {
+    // --fixed triggers replacing the comfiguration model.
+    static class TestArgsCustomModelAltArg implements 
FusekiServerArgsCustomiser {
         final ArgDecl argDecl;
         final Model fixedModel;
         Model notedServerConfigModel = null;
         boolean argSeen = false;
 
-        FusekiCliCustomModel(ArgDecl argDecl, Model conf) {
+        TestArgsCustomModelAltArg(ArgDecl argDecl, Model conf) {
             this.argDecl = argDecl;
             this.fixedModel = conf;
         }
 
         @Override
-        public void serverArgsModify(CmdGeneral fusekiCmd) {
+        public void serverArgsModify(CmdGeneral fusekiCmd, ServerArgs 
serverArgs) {
             fusekiCmd.add(argDecl);
         }
 
@@ -210,18 +259,23 @@ public class TestFusekiMainCmdCustomArguments {
         }
     };
 
-    static class FusekiCliCustomConfiguration implements 
FusekiServerArgsCustomiser {
-        @Override public void serverArgsModify(CmdGeneral fusekiCmd) { }
-        @Override public void serverArgsPrepare(CmdGeneral fusekiCmd, 
ServerArgs serverArgs) {
-            if ( Objects.equals(serverArgs.serverConfigFile, "placeholder") ) {
+    // Replace --conf setting with the model. Do nothing if no --conf.
+    static class TestArgsMyConfModel implements FusekiServerArgsCustomiser {
+
+        TestArgsMyConfModel() { }
+
+        @Override
+        public void serverArgsModify(CmdGeneral fusekiCmd, ServerArgs 
serverArgs) {}
+
+        @Override
+        public void serverArgsPrepare(CmdGeneral fusekiCmd, ServerArgs 
serverArgs) {
+            if ( serverArgs.serverConfigFile != null ) {
+                serverArgs.serverConfigFile = null;
                 serverArgs.serverConfigModel = confFixed;
             }
-
-            //serverArgs.serverConfigFile;
-            //serverArgs.serverConfigModel;
         }
 
-        @Override public void serverArgsBuilder(FusekiServer.Builder 
serverBuilder, Model configModel) {}
-    }
-
+        @Override
+        public void serverArgsBuilder(FusekiServer.Builder serverBuilder, 
Model configModel) {}
+    };
 }
diff --git 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/sys/TestFusekiModules.java
 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/sys/TestFusekiModules.java
index d5ceea9e18..75e48612cc 100644
--- 
a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/sys/TestFusekiModules.java
+++ 
b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/sys/TestFusekiModules.java
@@ -25,13 +25,12 @@ import static org.junit.Assert.assertTrue;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import org.apache.jena.atlas.logging.LogCtl;
-import org.apache.jena.fuseki.Fuseki;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
 import org.apache.jena.fuseki.main.FusekiServer;
 import org.apache.jena.rdf.model.Model;
 import org.apache.jena.sys.JenaSystem;
-import org.junit.BeforeClass;
-import org.junit.Test;
 
 /** Same packege for access */
 public class TestFusekiModules {
@@ -48,23 +47,22 @@ public class TestFusekiModules {
     }
 
     private static void reset() {
-        FusekiAutoModules.reset();
+        ModuleByServiceLoader.reset();
+        FusekiModules.resetSystemDefault();
     }
 
     @Test public void lifecycle_1() {
-        reset();
-
         ModuleForTest module = new ModuleForTest();
         FusekiModules fmods = FusekiModules.create(module);
 
         // Mock default set.
-        FusekiAutoModules.setSystemDefault(fmods);
+        FusekiModules.setSystemDefault(fmods);
 
         FusekiServer.Builder builder = FusekiServer.create().port(0);
         try {
             lifecycle(builder, module);
         } finally {
-            FusekiAutoModules.setSystemDefault(null);
+            FusekiModules.setSystemDefault(null);
         }
     }
 
@@ -73,7 +71,6 @@ public class TestFusekiModules {
 
         ModuleForTest module = new ModuleForTest();
         FusekiModules fmods = FusekiModules.create(module);
-        FusekiAutoModules.setSystemDefault(null);
         // Explicit FusekiModules
         FusekiServer.Builder builder = 
FusekiServer.create().fusekiModules(fmods).port(0);
         lifecycle(builder, module);
@@ -107,23 +104,18 @@ public class TestFusekiModules {
     }
 
     @Test public void autoload_1() {
-        reset();
+        // Included reload.
         ModuleByServiceLoader.reset();
+        FusekiModules.resetSystemDefault();
 
-        // Default : loaded FusekiAutoModules
-        FusekiServer.Builder builder = FusekiServer.create().port(0);
-        //Generates "warn"
-        LogCtl.withLevel(Fuseki.serverLog, "error", ()->{
-            builder.build();
-        });
-        ModuleForTest module = ModuleByServiceLoader.lastLoaded();
-
+        // Reloaded by FusekiModules.resetSystemDefault
         assertEquals(1, ModuleByServiceLoader.countLoads.get());
         assertEquals(1, ModuleByServiceLoader.countStart.get());
 
-        assertEquals("prepare:",       1, module.countPrepared.getPlain());
-        assertEquals("configured:",    1, module.countConfiguration.get());
-        assertEquals("server: ",       1, module.countServer.get());
+        // Default : loaded FusekiModules
+        FusekiServer.Builder builder = FusekiServer.create().port(0);
+        ModuleForTest module = ModuleByServiceLoader.lastLoaded();
+        lifecycle(builder, module);
     }
 
     @Test public void server_module_1() {

Reply via email to