diff --git a/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicCmd.java b/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicCmd.java
index f1ba1f2d631..72f0879afe8 100644
--- a/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicCmd.java
+++ b/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicCmd.java
@@ -18,52 +18,9 @@
package org.apache.jena.fuseki.cmds;
-import java.net.BindException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import arq.cmdline.CmdARQ;
-import arq.cmdline.ModAssembler;
-import arq.cmdline.ModDatasetAssembler;
-import jena.cmd.ArgDecl;
-import jena.cmd.CmdException;
-import org.apache.jena.assembler.exceptions.AssemblerException;
-import org.apache.jena.atlas.lib.DateTimeUtils ;
-import org.apache.jena.atlas.lib.FileOps;
-import org.apache.jena.atlas.logging.FmtLog;
-import org.apache.jena.fuseki.Fuseki;
-import org.apache.jena.fuseki.FusekiException;
-import org.apache.jena.fuseki.embedded.FusekiServer;
-import org.apache.jena.fuseki.server.DataAccessPoint;
-import org.apache.jena.fuseki.server.DataAccessPointRegistry;
-import org.apache.jena.fuseki.server.DataService;
-import org.apache.jena.fuseki.servlets.SPARQL_QueryGeneral ;
import org.apache.jena.fuseki.system.FusekiLogging;
-import org.apache.jena.fuseki.validation.DataValidator ;
-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.riot.Lang;
-import org.apache.jena.riot.RDFDataMgr;
-import org.apache.jena.riot.RDFLanguages;
-import org.apache.jena.sparql.core.DatasetGraph;
-import org.apache.jena.sparql.core.DatasetGraphFactory;
-import org.apache.jena.sys.JenaSystem;
-import org.apache.jena.system.Txn;
-import org.apache.jena.tdb.TDB;
-import org.apache.jena.tdb.TDBFactory;
-import org.apache.jena.tdb.transaction.TransactionManager;
-import org.apache.jena.tdb2.DatabaseMgr;
-import org.slf4j.Logger;
-/** Fuseki command that runs a Fuseki server with no UI, just SPARQL services.
+/** Fuseki command that runs a Fuseki server without the admin UI, just SPARQL services.
*
* Use {@code --conf=} for multiple datasets and specific service names.
*
@@ -71,489 +28,19 @@
*/
public class FusekiBasicCmd {
- // Own module (or optional dependency on jena-cmds)
- // Needs jena-cmds.
+ // This class wraps FusekiBasicMain so that it can take control of logging setup.
+ // It does not depend via inheritance on any Jena code - FusekiBasicMain does.
+ // Inheritance causes initialization in the super class first, before class
+ // initialization code in this class.
- static {
- FusekiLogging.setLogging();
- }
+ static { FusekiLogging.setLogging(); }
- /** Build and run, a server based on command line syntax. This operation does not return. */
+ /**
+ * Build and run, a server based on command line syntax. This operation does not
+ * return. See {@link FusekiBasicMain#build} to build a server using command line
+ * syntax but not start it.
+ */
static public void main(String... argv) {
- FusekiCmdInner.innerMain(argv);
- }
-
- /** Build, but do not start, a server based on command line syntax. */
- static public FusekiServer build(String... argv) {
- return FusekiCmdInner.build(argv);
- }
-
- /** Dataset setup (command line, config file) for a dataset (or several if config file) */
- static class ServerConfig {
- public int port;
- // Dataset name on the command line.
- public String datasetPath = null;
- // Command line --update.
- public boolean allowUpdate = false;
-
- // This is set ...
- public DatasetGraph dsg = null;
- // ... or this.
- public String serverConfig = null;
-
-
- // Allow there to be no registered datasets without it being an error.
- // which is "return dsg==null && serverConfig==null;"
- public boolean empty = false ;
- // Setup for SPARQLer - validators and general query engine, some pages.
- public boolean sparqler = false ;
- public boolean validators = false ;
- public boolean loopback = false;
- public String datasetDescription;
- public String contentDirectory = null;
- }
-
- static class FusekiCmdInner extends CmdARQ {
- private static int defaultPort = 3030;
-
- private static ArgDecl argMem = new ArgDecl(ArgDecl.NoValue, "mem");
- private static ArgDecl argUpdate = new ArgDecl(ArgDecl.NoValue, "update", "allowUpdate");
- private static ArgDecl argFile = new ArgDecl(ArgDecl.HasValue, "file");
-
- private static ArgDecl argTDB2mode = new ArgDecl(ArgDecl.NoValue, "tdb2");
- private static ArgDecl argMemTDB = new ArgDecl(ArgDecl.NoValue, "memtdb", "memTDB", "tdbmem");
- private static ArgDecl argTDB = new ArgDecl(ArgDecl.HasValue, "loc", "location", "tdb");
-
- // No SPARQL dataset or services
- private static ArgDecl argEmpty = new ArgDecl(ArgDecl.NoValue, "empty", "no-dataset");
- private static ArgDecl argPort = new ArgDecl(ArgDecl.HasValue, "port");
- private static ArgDecl argLocalhost = new ArgDecl(ArgDecl.NoValue, "localhost", "local");
- private static ArgDecl argTimeout = new ArgDecl(ArgDecl.HasValue, "timeout");
- private static ArgDecl argConfig = new ArgDecl(ArgDecl.HasValue, "config", "conf");
- private static ArgDecl argGZip = new ArgDecl(ArgDecl.HasValue, "gzip");
- private static ArgDecl argBase = new ArgDecl(ArgDecl.HasValue, "base", "files");
- private static ArgDecl argSparqler = new ArgDecl(ArgDecl.HasValue, "sparqler");
- private static ArgDecl argValidators = new ArgDecl(ArgDecl.NoValue, "validators");
- // private static ModLocation modLocation = new ModLocation();
- private static ModDatasetAssembler modDataset = new ModDatasetAssembler();
-
- static void innerMain(String... argv) {
- JenaSystem.init();
- new FusekiCmdInner(argv).mainRun();
- }
-
- /** Build, but do not start, a server based on command line syntax. */
- static FusekiServer build(String... argv) {
- FusekiCmdInner inner = new FusekiCmdInner(argv);
- inner.process();
- return inner.buildServer();
- }
-
- private final ServerConfig serverConfig = new ServerConfig();
- private boolean useTDB2;
-
- public FusekiCmdInner(String... argv) {
- super(argv);
-
- if ( false )
- // Consider ...
- TransactionManager.QueueBatchSize = TransactionManager.QueueBatchSize / 2;
-
- getUsage().startCategory("Fuseki");
- addModule(modDataset);
- add(argMem, "--mem",
- "Create an in-memory, non-persistent dataset for the server");
- add(argFile, "--file=FILE",
- "Create an in-memory, non-persistent dataset for the server, initialised with the contents of the file");
- add(argTDB2mode, "--tdb2",
- "Create command line persistent datasets with TDB2");
- add(argTDB, "--loc=DIR",
- "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 (validators only)");
- add(argEmpty); // Hidden for now.
- add(argPort, "--port",
- "Listen on this port number");
- add(argLocalhost, "--localhost",
- "Listen only on the localhost interface");
- add(argTimeout, "--timeout=",
- "Global timeout applied to queries (value in ms) -- format is X[,Y] ");
- add(argUpdate, "--update",
- "Allow updates (via SPARQL Update and SPARQL HTTP Update)");
- add(argConfig, "--config=",
- "Use a configuration file to determine the services");
- add(argGZip, "--gzip=on|off",
- "Enable GZip compression (HTTP Accept-Encoding) if request header set");
- add(argBase, "--base=DIR",
- "Directory for static content");
- add(argSparqler, "--sparqler=DIR",
- "Run with SPARQLer services Directory for static content");
- add(argValidators, "--validators", "Install validators");
-
- super.modVersion.addClass(TDB.class);
- super.modVersion.addClass(Fuseki.class);
- }
-
- static String argUsage = "[--config=FILE] [--mem|--desc=AssemblerFile|--file=FILE] [--port PORT] /DatasetPathName";
-
- @Override
- protected String getSummary() {
- return getCommandName() + " " + argUsage;
- }
-
- @Override
- protected void processModulesAndArgs() {
- int x = 0;
-
- Logger log = Fuseki.serverLog;
-
- // ---- Checking
-
- if ( contains(argMem) )
- x++;
- if ( contains(argFile) )
- x++;
- if ( contains(ModAssembler.assemblerDescDecl) )
- x++;
- if ( contains(argTDB) )
- x++;
- if ( contains(argMemTDB) )
- x++;
- if ( contains(argConfig) )
- x++;
-
- boolean allowEmpty = contains(argEmpty) || contains(argSparqler);
-
-
- if ( x == 0 && ! allowEmpty )
- throw new CmdException("No dataset specified on the command line.");
-
- if ( x > 1 )
- throw new CmdException("Multiple ways providing a dataset. Only one of --mem, --file, --loc or --desc");
-
- if ( x > 0 && allowEmpty )
- throw new CmdException("Dataset provided but 'no dataset' flag given");
-
- //---- check: Invalid: --conf + service name.
- if ( contains(argConfig) ) {
- if ( getPositional().size() != 0 )
- throw new CmdException("Can't have both a configutation file and a service name");
- } else if ( ! allowEmpty ) {
- if ( getPositional().size() == 0 )
- throw new CmdException("Missing service name");
- if ( getPositional().size() > 1 )
- throw new CmdException("Multiple dataset path names given");
- serverConfig.datasetPath = DataAccessPoint.canonical(getPositionalArg(0));
- }
-
- serverConfig.datasetDescription = "";
-
- // ---- check: Invalid: --update + --conf
- if ( contains(argUpdate) && contains(argConfig) )
- throw new CmdException("--update and a configuration file does not make sense (control using the configuration file only)");
- boolean allowUpdate = contains(argUpdate);
- serverConfig.allowUpdate = allowUpdate;
-
- // ---- Port
- serverConfig.port = defaultPort;
-
- if ( contains(argPort) ) {
- String portStr = getValue(argPort);
- try {
- int port = Integer.parseInt(portStr);
- serverConfig.port = port;
- } catch (NumberFormatException ex) {
- throw new CmdException(argPort.getKeyName() + " : bad port number: " + portStr);
- }
- }
- if ( contains(argLocalhost) )
- serverConfig.loopback = true;
-
- // ---- Dataset
- // Only one of these is choose from the checking above.
-
- // Which TDB to use to create a command line TDB database.
- useTDB2 = contains(argTDB2mode);
- String tag = useTDB2 ? "TDB2" : "TDB";
-
- if ( allowEmpty ) {
- serverConfig.empty = true;
- serverConfig.datasetDescription = "No dataset";
- }
-
- // Fuseki config file
- if ( contains(argConfig) ) {
- String file = getValue(argConfig);
- if ( file.startsWith("file:") )
- file = file.substring("file:".length());
-
- Path path = Paths.get(file);
- if ( ! Files.exists(path) )
- throw new CmdException("File not found: "+file);
- if ( Files.isDirectory(path) )
- throw new CmdException("Is a directory: "+file);
- serverConfig.datasetDescription = "Configuration: "+path.toAbsolutePath();
- serverConfig.serverConfig = getValue(argConfig);
- }
-
- // Ways to setup a dataset.
- if ( contains(argMem) ) {
- serverConfig.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.
- serverConfig.dsg = DatasetGraphFactory.createTxnMem();
- // Always allow, else you can't do very much!
- serverConfig.allowUpdate = true;
- }
-
- if ( contains(argFile) ) {
- String filename = getValue(argFile);
- String pathname = filename;
- if ( filename.startsWith("file:") )
- pathname = filename.substring("file:".length());
-
- serverConfig.datasetDescription = "file:"+filename;
- if ( !FileOps.exists(pathname) )
- throw new CmdException("File not found: " + filename);
- serverConfig.dsg = DatasetGraphFactory.createTxnMem();
-
- // INITIAL DATA.
- Lang language = RDFLanguages.filenameToLang(filename);
- if ( language == null )
- throw new CmdException("Can't guess language for file: " + filename);
- Txn.executeWrite(serverConfig.dsg, ()->RDFDataMgr.read(serverConfig.dsg, filename));
- }
-
-// if ( contains(argMemTDB) ) {
-// //log.info("TDB dataset: in-memory") ;
-// cmdLineConfig.reset();
-// cmdLineConfig.argTemplateFile = useTDB2 ? Template.templateTDB2_MemFN : Template.templateTDB1_MemFN ;
-// cmdLineConfig.params.put(Template.DIR, Names.memName) ;
-// // Always allow.
-// cmdLineConfig.allowUpdate = true ;
-// cmdLineConfig.datasetDescription = useTDB2 ? "TDB2 dataset (in-memory)" : "TDB dataset (in-memory)";
-// }
-//
-// if ( contains(argTDB) ) {
-// cmdLineConfig.reset();
-// cmdLineConfig.argTemplateFile =
-// useTDB2 ? Template.templateTDB2_DirFN : Template.templateTDB1_DirFN;
-// String dir = getValue(argTDB) ;
-// cmdLineConfig.params.put(Template.DIR, dir) ;
-// cmdLineConfig.datasetDescription = useTDB2 ? "TDB2 dataset: "+dir : "TDB dataset: "+dir;
-// }
-
- if ( contains(argMemTDB) ) {
- serverConfig.datasetDescription = tag+" dataset in-memory";
- serverConfig.dsg =
- useTDB2
- ? DatabaseMgr.createDatasetGraph()
- : TDBFactory.createDatasetGraph();
- serverConfig.allowUpdate = true;
- }
-
- if ( contains(argTDB) ) {
- String dir = getValue(argTDB);
- serverConfig.datasetDescription = tag+" dataset: "+dir;
- serverConfig.dsg =
- useTDB2
- ? DatabaseMgr.connectDatasetGraph(dir)
- : TDBFactory.createDatasetGraph(dir);
- }
-
- if ( contains(ModAssembler.assemblerDescDecl) ) {
- serverConfig.datasetDescription = "Assembler: "+ getValue(ModAssembler.assemblerDescDecl);
- // Need to add service details.
- Dataset ds = modDataset.createDataset();
- serverConfig.dsg = ds.asDatasetGraph();
- }
-
- // ---- Misc features.
- if ( contains(argTimeout) ) {
- String str = getValue(argTimeout);
- ARQ.getContext().set(ARQ.queryTimeout, str);
- }
-
- if ( contains(argValidators) ) {
- serverConfig.validators = true;
- }
-
- if ( contains(argSparqler) ) {
- String filebase = getValue(argSparqler);
- if ( ! FileOps.exists(filebase) )
- throw new CmdException("File area not found: "+filebase);
- serverConfig.contentDirectory = filebase;
- serverConfig.sparqler = true;
- serverConfig.validators = true;
- }
-
- if ( contains(argBase) ) {
- // Static files.
- String filebase = getValue(argBase);
- if ( ! FileOps.exists(filebase) ) {
- throw new CmdException("File area not found: "+filebase);
- //FmtLog.warn(Fuseki.configLog, "File area not found: "+filebase);
- }
- serverConfig.contentDirectory = filebase;
- }
-
-// if ( contains(argGZip) ) {
-// if ( !hasValueOfTrue(argGZip) && !hasValueOfFalse(argGZip) )
-// throw new CmdException(argGZip.getNames().get(0) + ": Not understood: " + getValue(argGZip));
-// jettyServerConfig.enableCompression = super.hasValueOfTrue(argGZip);
-// }
- }
-
-// private static String sort_out_dir(String path) {
-// path.replace('\\', '/');
-// if ( !path.endsWith("/") )
-// path = path + "/";
-// return path;
-// }
-
- @Override
- protected void exec() {
- try {
- FusekiServer server = buildServer(serverConfig);
- info(server, serverConfig);
- try {
- server.start();
- } catch (FusekiException ex) {
- if ( ex.getCause() instanceof BindException ) {
- Fuseki.serverLog.error("Failed to start server: "+ex.getCause().getMessage()+ ": port="+serverConfig.port) ;
- System.exit(1);
- }
- throw ex;
- } catch (Exception ex) {
- throw new FusekiException("Failed to start server: " + ex.getMessage(), ex) ;
- }
- server.join();
- System.exit(0);
- }
- catch (AssemblerException ex) {
- if ( ex.getCause() != null )
- System.err.println(ex.getCause().getMessage());
- else
- System.err.println(ex.getMessage());
- }
- }
-
- private FusekiServer buildServer() {
- return buildServer(serverConfig);
- }
-
- // ServerConfig -> Setup the builder.
- private static FusekiServer buildServer(ServerConfig serverConfig) {
- FusekiServer.Builder builder = FusekiServer.create();
- // Loopback.
- builder.port(serverConfig.port);
- builder.loopback(serverConfig.loopback);
-
- if ( serverConfig.validators ) {
- if ( serverConfig.sparqler )
- builder.addServlet("/sparql", new SPARQL_QueryGeneral());
- // Validators.
- builder.addServlet("/validate/query", new QueryValidator());
- builder.addServlet("/validate/update", new UpdateValidator());
- builder.addServlet("/validate/iri", new IRIValidator());
- builder.addServlet("/validate/data", new DataValidator());
- }
- if ( ! serverConfig.empty ) {
- if ( serverConfig.serverConfig != null )
- // Config file.
- builder.parseConfigFile(serverConfig.serverConfig);
- else
- // One dataset.
- builder.add(serverConfig.datasetPath, serverConfig.dsg, serverConfig.allowUpdate);
- }
-
- if ( serverConfig.contentDirectory != null )
- builder.staticFileBase(serverConfig.contentDirectory) ;
-
- return builder.build();
- }
-
- private void info(FusekiServer server, ServerConfig serverConfig) {
- if ( super.isQuiet() )
- return;
-
- Logger log = Fuseki.serverLog;
-
- String version = Fuseki.VERSION;
- String buildDate = Fuseki.BUILD_DATE ;
-
- if ( version != null && version.equals("${project.version}") )
- version = null ;
- if ( buildDate != null && buildDate.equals("${build.time.xsd}") )
- buildDate = DateTimeUtils.nowAsXSDDateTimeString() ;
-
- String name = Fuseki.NAME;
- name = name +" (basic server)";
-
- if ( version != null ) {
- if ( Fuseki.developmentMode && buildDate != null )
- FmtLog.info(log, "%s %s %s", name, version, buildDate) ;
- else
- FmtLog.info(log, "%s %s", name, version);
- }
-
- // Dataset -> Endpoints
- Map> mapDatasetEndpoints = description(DataAccessPointRegistry.get(server.getServletContext()));
-
- if ( serverConfig.empty ) {
- FmtLog.info(log, "No SPARQL datasets services");
- } else {
- if ( serverConfig.datasetPath == null && serverConfig.serverConfig == null )
- log.error("No dataset path nor server configuration file");
- }
-
- if ( serverConfig.datasetPath != null ) {
- if ( mapDatasetEndpoints.size() != 1 )
- log.error("Expected only one dataset");
- List endpoints = mapDatasetEndpoints.get(serverConfig.datasetPath);
- FmtLog.info(log, "Dataset Type = %s", serverConfig.datasetDescription);
- FmtLog.info(log, "Path = %s; Services = %s", serverConfig.datasetPath, endpoints);
- }
- if ( serverConfig.serverConfig != null ) {
- // May be many datasets and services.
- FmtLog.info(log, "Configuration file %s", serverConfig.serverConfig);
- mapDatasetEndpoints.forEach((path, endpoints)->{
- FmtLog.info(log, "Path = %s; Services = %s", path, endpoints);
- });
- }
-
- if ( serverConfig.contentDirectory != null )
- FmtLog.info(log, "Static files = %s", serverConfig.contentDirectory);
-
- if ( super.isVerbose() )
- PlatformInfo.logDetailsVerbose(log);
- else if ( !super.isQuiet() )
- PlatformInfo.logDetails(log);
- }
-
- private static Map> description(DataAccessPointRegistry reg) {
- Map> desc = new LinkedHashMap<>();
- reg.forEach((ds,dap)->{
- List endpoints = new ArrayList<>();
- desc.put(ds, endpoints);
- DataService dSrv = dap.getDataService();
- dSrv.getOperations().forEach((op)->{
- dSrv.getEndpoints(op).forEach(ep-> {
- String x = ep.getEndpoint();
- if ( x.isEmpty() )
- x = "quads";
- endpoints.add(x);
- });
- });
- });
- return desc;
- }
-
- @Override
- protected String getCommandName() {
- return "fuseki";
- }
+ FusekiBasicMain.innerMain(argv);
}
}
diff --git a/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicMain.java b/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicMain.java
new file mode 100644
index 00000000000..6febd2604e9
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicMain.java
@@ -0,0 +1,513 @@
+/*
+ * 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.cmds;
+
+import java.net.BindException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import arq.cmdline.CmdARQ;
+import arq.cmdline.ModAssembler;
+import arq.cmdline.ModDatasetAssembler;
+import jena.cmd.ArgDecl;
+import jena.cmd.CmdException;
+import org.apache.jena.assembler.exceptions.AssemblerException;
+import org.apache.jena.atlas.lib.DateTimeUtils;
+import org.apache.jena.atlas.lib.FileOps;
+import org.apache.jena.atlas.logging.FmtLog;
+import org.apache.jena.fuseki.Fuseki;
+import org.apache.jena.fuseki.FusekiException;
+import org.apache.jena.fuseki.embedded.FusekiServer;
+import org.apache.jena.fuseki.server.DataAccessPoint;
+import org.apache.jena.fuseki.server.DataAccessPointRegistry;
+import org.apache.jena.fuseki.server.DataService;
+import org.apache.jena.fuseki.servlets.SPARQL_QueryGeneral;
+import org.apache.jena.fuseki.validation.DataValidator;
+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.riot.Lang;
+import org.apache.jena.riot.RDFDataMgr;
+import org.apache.jena.riot.RDFLanguages;
+import org.apache.jena.sparql.core.DatasetGraphFactory;
+import org.apache.jena.sys.JenaSystem;
+import org.apache.jena.system.Txn;
+import org.apache.jena.tdb.TDB;
+import org.apache.jena.tdb.TDBFactory;
+import org.apache.jena.tdb.transaction.TransactionManager;
+import org.apache.jena.tdb2.DatabaseMgr;
+import org.slf4j.Logger;
+
+public class FusekiBasicMain extends CmdARQ {
+ private static int defaultPort = 3030;
+
+ private static ArgDecl argMem = new ArgDecl(ArgDecl.NoValue, "mem");
+ private static ArgDecl argUpdate = new ArgDecl(ArgDecl.NoValue, "update", "allowUpdate");
+ private static ArgDecl argFile = new ArgDecl(ArgDecl.HasValue, "file");
+
+ private static ArgDecl argTDB2mode = new ArgDecl(ArgDecl.NoValue, "tdb2");
+ private static ArgDecl argMemTDB = new ArgDecl(ArgDecl.NoValue, "memtdb", "memTDB", "tdbmem");
+ private static ArgDecl argTDB = new ArgDecl(ArgDecl.HasValue, "loc", "location", "tdb");
+
+ // No SPARQL dataset or services
+ private static ArgDecl argEmpty = new ArgDecl(ArgDecl.NoValue, "empty", "no-dataset");
+ private static ArgDecl argPort = new ArgDecl(ArgDecl.HasValue, "port");
+ private static ArgDecl argLocalhost = new ArgDecl(ArgDecl.NoValue, "localhost", "local");
+ private static ArgDecl argTimeout = new ArgDecl(ArgDecl.HasValue, "timeout");
+ private static ArgDecl argConfig = new ArgDecl(ArgDecl.HasValue, "config", "conf");
+ private static ArgDecl argGZip = new ArgDecl(ArgDecl.HasValue, "gzip");
+ private static ArgDecl argBase = new ArgDecl(ArgDecl.HasValue, "base", "files");
+ private static ArgDecl argSparqler = new ArgDecl(ArgDecl.HasValue, "sparqler");
+ private static ArgDecl argValidators = new ArgDecl(ArgDecl.NoValue, "validators");
+ // private static ModLocation modLocation = new ModLocation();
+ private static ModDatasetAssembler modDataset = new ModDatasetAssembler();
+
+ private final ServerConfig serverConfig = new ServerConfig();
+ private boolean useTDB2;
+
+ /** Build, but do not start, a server based on command line syntax. */
+ public static FusekiServer build(String... argv) {
+ FusekiBasicMain inner = new FusekiBasicMain(argv);
+ inner.process();
+ return inner.buildServer();
+ }
+
+ static void innerMain(String... argv) {
+ JenaSystem.init();
+ new FusekiBasicMain(argv).mainRun();
+ }
+
+ protected FusekiBasicMain(String... argv) {
+ super(argv);
+
+ if ( false )
+ // Consider ...
+ TransactionManager.QueueBatchSize = TransactionManager.QueueBatchSize / 2;
+
+ getUsage().startCategory("Fuseki");
+ addModule(modDataset);
+ add(argMem, "--mem",
+ "Create an in-memory, non-persistent dataset for the server");
+ add(argFile, "--file=FILE",
+ "Create an in-memory, non-persistent dataset for the server, initialised with the contents of the file");
+ add(argTDB2mode, "--tdb2",
+ "Create command line persistent datasets with TDB2");
+ add(argTDB, "--loc=DIR",
+ "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 (validators only)");
+ add(argEmpty); // Hidden for now.
+ add(argPort, "--port",
+ "Listen on this port number");
+ add(argLocalhost, "--localhost",
+ "Listen only on the localhost interface");
+ add(argTimeout, "--timeout=",
+ "Global timeout applied to queries (value in ms) -- format is X[,Y] ");
+ add(argUpdate, "--update",
+ "Allow updates (via SPARQL Update and SPARQL HTTP Update)");
+ add(argConfig, "--config=",
+ "Use a configuration file to determine the services");
+ add(argGZip, "--gzip=on|off",
+ "Enable GZip compression (HTTP Accept-Encoding) if request header set");
+ add(argBase, "--base=DIR",
+ "Directory for static content");
+ add(argSparqler, "--sparqler=DIR",
+ "Run with SPARQLer services Directory for static content");
+ add(argValidators, "--validators", "Install validators");
+
+ super.modVersion.addClass(TDB.class);
+ super.modVersion.addClass(Fuseki.class);
+ }
+
+ static String argUsage = "[--config=FILE] [--mem|--desc=AssemblerFile|--file=FILE] [--port PORT] /DatasetPathName";
+
+ @Override
+ protected String getSummary() {
+ return getCommandName() + " " + argUsage;
+ }
+
+ @Override
+ protected void processModulesAndArgs() {
+ int x = 0;
+
+ Logger log = Fuseki.serverLog;
+
+ // ---- Checking
+
+ if ( contains(argMem) )
+ x++;
+ if ( contains(argFile) )
+ x++;
+ if ( contains(ModAssembler.assemblerDescDecl) )
+ x++;
+ if ( contains(argTDB) )
+ x++;
+ if ( contains(argMemTDB) )
+ x++;
+ if ( contains(argConfig) )
+ x++;
+
+ boolean allowEmpty = contains(argEmpty) || contains(argSparqler);
+
+
+ if ( x == 0 && ! allowEmpty )
+ throw new CmdException("No dataset specified on the command line.");
+
+ if ( x > 1 )
+ throw new CmdException("Multiple ways providing a dataset. Only one of --mem, --file, --loc or --desc");
+
+ if ( x > 0 && allowEmpty )
+ throw new CmdException("Dataset provided but 'no dataset' flag given");
+
+ //---- check: Invalid: --conf + service name.
+ if ( contains(argConfig) ) {
+ if ( getPositional().size() != 0 )
+ throw new CmdException("Can't have both a configutation file and a service name");
+ } else if ( ! allowEmpty ) {
+ if ( getPositional().size() == 0 )
+ throw new CmdException("Missing service name");
+ if ( getPositional().size() > 1 )
+ throw new CmdException("Multiple dataset path names given");
+ serverConfig.datasetPath = DataAccessPoint.canonical(getPositionalArg(0));
+ }
+
+ serverConfig.datasetDescription = "";
+
+ // ---- check: Invalid: --update + --conf
+ if ( contains(argUpdate) && contains(argConfig) )
+ throw new CmdException("--update and a configuration file does not make sense (control using the configuration file only)");
+ boolean allowUpdate = contains(argUpdate);
+ serverConfig.allowUpdate = allowUpdate;
+
+ // ---- Port
+ serverConfig.port = defaultPort;
+
+ if ( contains(argPort) ) {
+ String portStr = getValue(argPort);
+ try {
+ int port = Integer.parseInt(portStr);
+ serverConfig.port = port;
+ } catch (NumberFormatException ex) {
+ throw new CmdException(argPort.getKeyName() + " : bad port number: " + portStr);
+ }
+ }
+ if ( contains(argLocalhost) )
+ serverConfig.loopback = true;
+
+ // ---- Dataset
+ // Only one of these is choose from the checking above.
+
+ // Which TDB to use to create a command line TDB database.
+ useTDB2 = contains(argTDB2mode);
+ String tag = useTDB2 ? "TDB2" : "TDB";
+
+ if ( allowEmpty ) {
+ serverConfig.empty = true;
+ serverConfig.datasetDescription = "No dataset";
+ }
+
+ // Fuseki config file
+ if ( contains(argConfig) ) {
+ String file = getValue(argConfig);
+ if ( file.startsWith("file:") )
+ file = file.substring("file:".length());
+
+ Path path = Paths.get(file);
+ if ( ! Files.exists(path) )
+ throw new CmdException("File not found: "+file);
+ if ( Files.isDirectory(path) )
+ throw new CmdException("Is a directory: "+file);
+ serverConfig.datasetDescription = "Configuration: "+path.toAbsolutePath();
+ serverConfig.serverConfig = getValue(argConfig);
+ }
+
+ // Ways to setup a dataset.
+ if ( contains(argMem) ) {
+ serverConfig.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.
+ serverConfig.dsg = DatasetGraphFactory.createTxnMem();
+ // Always allow, else you can't do very much!
+ serverConfig.allowUpdate = true;
+ }
+
+ if ( contains(argFile) ) {
+ String filename = getValue(argFile);
+ String pathname = filename;
+ if ( filename.startsWith("file:") )
+ pathname = filename.substring("file:".length());
+
+ serverConfig.datasetDescription = "file:"+filename;
+ if ( !FileOps.exists(pathname) )
+ throw new CmdException("File not found: " + filename);
+ serverConfig.dsg = DatasetGraphFactory.createTxnMem();
+
+ // INITIAL DATA.
+ Lang language = RDFLanguages.filenameToLang(filename);
+ if ( language == null )
+ throw new CmdException("Can't guess language for file: " + filename);
+ Txn.executeWrite(serverConfig.dsg, ()->RDFDataMgr.read(serverConfig.dsg, filename));
+ }
+
+// if ( contains(argMemTDB) ) {
+// //log.info("TDB dataset: in-memory") ;
+// cmdLineConfig.reset();
+// cmdLineConfig.argTemplateFile = useTDB2 ? Template.templateTDB2_MemFN : Template.templateTDB1_MemFN ;
+// cmdLineConfig.params.put(Template.DIR, Names.memName) ;
+// // Always allow.
+// cmdLineConfig.allowUpdate = true ;
+// cmdLineConfig.datasetDescription = useTDB2 ? "TDB2 dataset (in-memory)" : "TDB dataset (in-memory)";
+// }
+//
+// if ( contains(argTDB) ) {
+// cmdLineConfig.reset();
+// cmdLineConfig.argTemplateFile =
+// useTDB2 ? Template.templateTDB2_DirFN : Template.templateTDB1_DirFN;
+// String dir = getValue(argTDB) ;
+// cmdLineConfig.params.put(Template.DIR, dir) ;
+// cmdLineConfig.datasetDescription = useTDB2 ? "TDB2 dataset: "+dir : "TDB dataset: "+dir;
+// }
+
+ if ( contains(argMemTDB) ) {
+ serverConfig.datasetDescription = tag+" dataset in-memory";
+ serverConfig.dsg =
+ useTDB2
+ ? DatabaseMgr.createDatasetGraph()
+ : TDBFactory.createDatasetGraph();
+ serverConfig.allowUpdate = true;
+ }
+
+ if ( contains(argTDB) ) {
+ String dir = getValue(argTDB);
+ serverConfig.datasetDescription = tag+" dataset: "+dir;
+ serverConfig.dsg =
+ useTDB2
+ ? DatabaseMgr.connectDatasetGraph(dir)
+ : TDBFactory.createDatasetGraph(dir);
+ }
+
+ if ( contains(ModAssembler.assemblerDescDecl) ) {
+ serverConfig.datasetDescription = "Assembler: "+ getValue(ModAssembler.assemblerDescDecl);
+ // Need to add service details.
+ Dataset ds = modDataset.createDataset();
+ serverConfig.dsg = ds.asDatasetGraph();
+ }
+
+ // ---- Misc features.
+ if ( contains(argTimeout) ) {
+ String str = getValue(argTimeout);
+ ARQ.getContext().set(ARQ.queryTimeout, str);
+ }
+
+ if ( contains(argValidators) ) {
+ serverConfig.validators = true;
+ }
+
+ if ( contains(argSparqler) ) {
+ String filebase = getValue(argSparqler);
+ if ( ! FileOps.exists(filebase) )
+ throw new CmdException("File area not found: "+filebase);
+ serverConfig.contentDirectory = filebase;
+ serverConfig.sparqler = true;
+ serverConfig.validators = true;
+ }
+
+ if ( contains(argBase) ) {
+ // Static files.
+ String filebase = getValue(argBase);
+ if ( ! FileOps.exists(filebase) ) {
+ throw new CmdException("File area not found: "+filebase);
+ //FmtLog.warn(Fuseki.configLog, "File area not found: "+filebase);
+ }
+ serverConfig.contentDirectory = filebase;
+ }
+
+// if ( contains(argGZip) ) {
+// if ( !hasValueOfTrue(argGZip) && !hasValueOfFalse(argGZip) )
+// throw new CmdException(argGZip.getNames().get(0) + ": Not understood: " + getValue(argGZip));
+// jettyServerConfig.enableCompression = super.hasValueOfTrue(argGZip);
+// }
+ }
+
+// private static String sort_out_dir(String path) {
+// path.replace('\\', '/');
+// if ( !path.endsWith("/") )
+// path = path + "/";
+// return path;
+// }
+
+ @Override
+ protected void exec() {
+ try {
+ FusekiServer server = buildServer(serverConfig);
+ info(server, serverConfig);
+ try {
+ server.start();
+ } catch (FusekiException ex) {
+ if ( ex.getCause() instanceof BindException ) {
+ Fuseki.serverLog.error("Failed to start server: "+ex.getCause().getMessage()+ ": port="+serverConfig.port) ;
+ System.exit(1);
+ }
+ throw ex;
+ } catch (Exception ex) {
+ throw new FusekiException("Failed to start server: " + ex.getMessage(), ex) ;
+ }
+ server.join();
+ System.exit(0);
+ }
+ catch (AssemblerException ex) {
+ if ( ex.getCause() != null )
+ System.err.println(ex.getCause().getMessage());
+ else
+ System.err.println(ex.getMessage());
+ }
+ }
+
+ private FusekiServer buildServer() {
+ return buildServer(serverConfig);
+ }
+
+ // ServerConfig -> Setup the builder.
+ private FusekiServer buildServer(ServerConfig serverConfig) {
+ FusekiServer.Builder builder = builder();
+ return buildServer(builder, serverConfig);
+ }
+
+ protected FusekiServer.Builder builder() {
+ return FusekiServer.create();
+ }
+
+ private static FusekiServer buildServer(FusekiServer.Builder builder, ServerConfig serverConfig) {
+ builder.port(serverConfig.port);
+ builder.loopback(serverConfig.loopback);
+
+ if ( serverConfig.validators ) {
+ if ( serverConfig.sparqler )
+ builder.addServlet("/sparql", new SPARQL_QueryGeneral());
+ // Validators.
+ builder.addServlet("/validate/query", new QueryValidator());
+ builder.addServlet("/validate/update", new UpdateValidator());
+ builder.addServlet("/validate/iri", new IRIValidator());
+ builder.addServlet("/validate/data", new DataValidator());
+ }
+ if ( ! serverConfig.empty ) {
+ if ( serverConfig.serverConfig != null )
+ // Config file.
+ builder.parseConfigFile(serverConfig.serverConfig);
+ else
+ // One dataset.
+ builder.add(serverConfig.datasetPath, serverConfig.dsg, serverConfig.allowUpdate);
+ }
+
+ if ( serverConfig.contentDirectory != null )
+ builder.staticFileBase(serverConfig.contentDirectory) ;
+
+ return builder.build();
+ }
+
+ private void info(FusekiServer server, ServerConfig serverConfig) {
+ if ( super.isQuiet() )
+ return;
+
+ Logger log = Fuseki.serverLog;
+
+ String version = Fuseki.VERSION;
+ String buildDate = Fuseki.BUILD_DATE ;
+
+ if ( version != null && version.equals("${project.version}") )
+ version = null ;
+ if ( buildDate != null && buildDate.equals("${build.time.xsd}") )
+ buildDate = DateTimeUtils.nowAsXSDDateTimeString() ;
+
+ String name = Fuseki.NAME;
+ name = name +" (basic server)";
+
+ if ( version != null ) {
+ if ( Fuseki.developmentMode && buildDate != null )
+ FmtLog.info(log, "%s %s %s", name, version, buildDate) ;
+ else
+ FmtLog.info(log, "%s %s", name, version);
+ }
+
+ // Dataset -> Endpoints
+ Map> mapDatasetEndpoints = description(DataAccessPointRegistry.get(server.getServletContext()));
+
+ if ( serverConfig.empty ) {
+ FmtLog.info(log, "No SPARQL datasets services");
+ } else {
+ if ( serverConfig.datasetPath == null && serverConfig.serverConfig == null )
+ log.error("No dataset path nor server configuration file");
+ }
+
+ if ( serverConfig.datasetPath != null ) {
+ if ( mapDatasetEndpoints.size() != 1 )
+ log.error("Expected only one dataset");
+ List endpoints = mapDatasetEndpoints.get(serverConfig.datasetPath);
+ FmtLog.info(log, "Dataset Type = %s", serverConfig.datasetDescription);
+ FmtLog.info(log, "Path = %s; Services = %s", serverConfig.datasetPath, endpoints);
+ }
+ if ( serverConfig.serverConfig != null ) {
+ // May be many datasets and services.
+ FmtLog.info(log, "Configuration file %s", serverConfig.serverConfig);
+ mapDatasetEndpoints.forEach((path, endpoints)->{
+ FmtLog.info(log, "Path = %s; Services = %s", path, endpoints);
+ });
+ }
+
+ if ( serverConfig.contentDirectory != null )
+ FmtLog.info(log, "Static files = %s", serverConfig.contentDirectory);
+
+ if ( super.isVerbose() )
+ PlatformInfo.logDetailsVerbose(log);
+ else if ( !super.isQuiet() )
+ PlatformInfo.logDetails(log);
+ }
+
+ private static Map> description(DataAccessPointRegistry reg) {
+ Map> desc = new LinkedHashMap<>();
+ reg.forEach((ds,dap)->{
+ List endpoints = new ArrayList<>();
+ desc.put(ds, endpoints);
+ DataService dSrv = dap.getDataService();
+ dSrv.getOperations().forEach((op)->{
+ dSrv.getEndpoints(op).forEach(ep-> {
+ String x = ep.getEndpoint();
+ if ( x.isEmpty() )
+ x = "quads";
+ endpoints.add(x);
+ });
+ });
+ });
+ return desc;
+ }
+
+ @Override
+ protected String getCommandName() {
+ return "fuseki";
+ }
+ }
\ No newline at end of file
diff --git a/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/ServerConfig.java b/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/ServerConfig.java
new file mode 100644
index 00000000000..e8301e6b307
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/ServerConfig.java
@@ -0,0 +1,51 @@
+/*
+ * 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.cmds;
+
+import org.apache.jena.sparql.core.DatasetGraph;
+
+/** Setup details (command line, config file) from command line processing.
+ * Thisis built by {@link FusekiBasicMain#exec}.
+ * This is processed by {@link FusekiBasicMain#buildServer}.
+ */
+class ServerConfig {
+ /** Server port */
+ public int port;
+ /** Loopback */
+ public boolean loopback = false;
+ /** The dataset name */
+ public String datasetPath = null;
+ /** Allow update */
+ public boolean allowUpdate = false;
+
+ // This is set ...
+ public DatasetGraph dsg = null;
+ // ... or this.
+ public String serverConfig = null;
+
+
+ /** Allow there to be no registered datasets without it being an error. */
+ public boolean empty = false ;
+ /** Setup for SPARQLer - validators and general query engine, some pages. */
+ public boolean sparqler = false ;
+ public boolean validators = false ;
+ /** An informative label */
+ public String datasetDescription;
+ public String contentDirectory = null;
+}
\ No newline at end of file