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