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() {
