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 dd17db6825fe5bdda977c1bdcdf3e7f38f9cea83 Author: Andy Seaborne <[email protected]> AuthorDate: Sat Dec 14 15:46:36 2024 +0000 GH-2462: Reload action for configuration files --- .../main/java/org/apache/jena/fuseki/Fuseki.java | 21 +++--- .../jena/fuseki/ctl/ActionContainerItem.java | 2 +- .../org/apache/jena/fuseki/ctl/ActionItem.java | 4 +- .../apache/jena/fuseki/ctl/ActionStatsText.java | 4 +- .../org/apache/jena/fuseki/ctl/ActionTasks.java | 2 +- .../fuseki/server/DataAccessPointRegistry.java | 16 ++++- .../org/apache/jena/fuseki/server/Dispatcher.java | 4 +- .../org/apache/jena/fuseki/servlets/ActionLib.java | 4 -- .../jena/fuseki/servlets/ActionProcessor.java | 4 +- .../jena/fuseki/servlets/BaseActionREST.java | 2 +- .../apache/jena/fuseki/servlets/HttpAction.java | 4 ++ .../apache/jena/fuseki/servlets/ServletOps.java | 11 ++- .../org/apache/jena/fuseki/servlets/UploadRDF.java | 2 +- .../apache/jena/fuseki/system/FusekiLogging.java | 7 +- .../org/apache/jena/fuseki/main/FusekiLib.java | 39 +++++++++-- .../org/apache/jena/fuseki/main/FusekiServer.java | 80 +++++++++++++++++++--- .../org/apache/jena/fuseki/main/JettyServer.java | 1 + .../jena/fuseki/main/sys/FusekiModuleStep.java | 2 +- .../apache/jena/fuseki/main/CustomTestService.java | 2 +- .../org/apache/jena/fuseki/main/TS_FusekiMain.java | 7 +- .../jena/fuseki/main/TestFusekiServerBuild.java | 7 +- .../apache/jena/fuseki/main/TestPlainServer.java | 2 +- .../jena/fuseki/main/examples/DemoService.java | 2 +- .../testing/Config/reload-config1.ttl | 16 +++++ .../testing/Config/reload-config2.ttl | 25 +++++++ .../jena-fuseki-main/testing/Files/exists.txt | 2 + .../jena-fuseki-main/testing/Files/file-top.txt | 2 - 27 files changed, 215 insertions(+), 59 deletions(-) diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/Fuseki.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/Fuseki.java index bd7c26431c..f82f7dad8f 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 @@ -40,37 +40,33 @@ import org.slf4j.LoggerFactory; public class Fuseki { // General fixed constants. - // See also FusekiServer for the naming on the filesystem /** Path as package name */ - static public String PATH = "org.apache.jena.fuseki"; + static public final String PATH = "org.apache.jena.fuseki"; /** a unique IRI for the Fuseki namespace */ - static public String FusekiIRI = "http://jena.apache.org/Fuseki"; + static public final String FusekiIRI = "http://jena.apache.org/Fuseki"; /** * a unique IRI including the symbol notation for which properties should be * appended */ - static public String FusekiSymbolIRI = "http://jena.apache.org/fuseki#"; + static public final String FusekiSymbolIRI = "http://jena.apache.org/fuseki#"; /** Default location of the pages for the Fuseki UI */ - static public String PagesStatic = "pages"; + static public final String PagesStatic = "pages"; /** Dummy base URi string for parsing SPARQL Query and Update requests */ - static public final String BaseParserSPARQL = "http://server/unset-base/"; + static public final String BaseParserSPARQL = "http://server/unset-base/"; /** Dummy base URi string for parsing SPARQL Query and Update requests */ - static public final String BaseUpload = "http://server/unset-base/"; - - /** Add CORS header */ - static public final boolean CORS_ENABLED = false; + static public final String BaseUpload = "http://server/unset-base/"; /** The name of the Fuseki server.*/ - static public final String NAME = "Apache Jena Fuseki"; + static public final String NAME = "Apache Jena Fuseki"; /** Version of this Fuseki instance */ - static public final String VERSION = Version.versionForClass(Fuseki.class).orElse("<development>"); + static public final String VERSION = Version.versionForClass(Fuseki.class).orElse("<development>"); /** Supporting Graph Store Protocol direct naming. * <p> @@ -178,6 +174,7 @@ 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"; + public static final String attrFusekiServer = "org.apache.jena.fuseki:Server"; public static void setVerbose(ServletContext cxt, boolean verbose) { cxt.setAttribute(attrVerbose, Boolean.valueOf(verbose)); diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionContainerItem.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionContainerItem.java index 1d62880090..8ed56532d3 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionContainerItem.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionContainerItem.java @@ -43,7 +43,7 @@ public abstract class ActionContainerItem extends ActionCtl { else if ( method.equals(METHOD_DELETE) ) performDelete(action); else - ServletOps.errorMethodNotAllowed(action.getMethod()); + ServletOps.errorMethodNotAllowed(action.getRequestMethod()); } @Override diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionItem.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionItem.java index ccc360ea47..dd32e8b50f 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionItem.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionItem.java @@ -30,14 +30,14 @@ public abstract class ActionItem extends ActionContainerItem @Override final protected JsonValue execGetContainer(HttpAction action) { - ServletOps.errorMethodNotAllowed(action.getMethod()); + ServletOps.errorMethodNotAllowed(action.getRequestMethod()); return null; } @Override final protected JsonValue execPostContainer(HttpAction action) { - ServletOps.errorMethodNotAllowed(action.getMethod()); + ServletOps.errorMethodNotAllowed(action.getRequestMethod()); return null; } } diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionStatsText.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionStatsText.java index 0ff5559b4b..a1e6b4c4a4 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionStatsText.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionStatsText.java @@ -38,12 +38,12 @@ public class ActionStatsText extends ActionCtl @Override public void validate(HttpAction action) { - switch(action.getMethod() ) { + switch(action.getRequestMethod() ) { case HttpNames.METHOD_GET: case HttpNames.METHOD_POST: return; default: - ServletOps.errorMethodNotAllowed(action.getMethod()); + ServletOps.errorMethodNotAllowed(action.getRequestMethod()); } } diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionTasks.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionTasks.java index 0d573b26ce..efbe6d475e 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionTasks.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionTasks.java @@ -62,7 +62,7 @@ public class ActionTasks extends ActionCtl else if ( method.equals(METHOD_POST) ) execPost(action, name); else - ServletOps.errorMethodNotAllowed(action.getMethod()); + ServletOps.errorMethodNotAllowed(action.getRequestMethod()); } private void execGet(HttpAction action, String name) { diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/DataAccessPointRegistry.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/DataAccessPointRegistry.java index b6d41f31da..02307d3e7e 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/DataAccessPointRegistry.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/DataAccessPointRegistry.java @@ -27,6 +27,7 @@ import org.apache.jena.atlas.lib.Registry; import org.apache.jena.atlas.logging.Log; import org.apache.jena.fuseki.Fuseki; import org.apache.jena.fuseki.FusekiException; +import org.apache.jena.fuseki.servlets.HttpAction; /** * Registry of (dataset name, {@link DataAccessPoint}). @@ -59,7 +60,7 @@ public class DataAccessPointRegistry extends Registry<String, DataAccessPoint> */ public List<DataAccessPoint> accessPoints() { List<DataAccessPoint> accessPoints = new ArrayList<>(size()); - // Make a copy for safety. + // Copy for safety. forEach((_name, accessPoint) -> accessPoints.add(accessPoint)); return accessPoints; } @@ -90,7 +91,14 @@ public class DataAccessPointRegistry extends Registry<String, DataAccessPoint> }); } - // The server DataAccessPointRegistry is held in the ServletContext for the server. + /** The server DataAccessPointRegistry is held in the ServletContext. + * <p> + * Reload may change this object for another one. Therefore, code should obtain the + * DataAccessPointRegistry once per operation. + * <p> + * Each request, has a stable {@link HttpAction#getDataAccessPointRegistry()}. + * <p>Getting the {@link DataAccessPointRegistry} is atomic. + */ public static DataAccessPointRegistry get(ServletContext cxt) { DataAccessPointRegistry registry = (DataAccessPointRegistry)cxt.getAttribute(Fuseki.attrNameRegistry); if ( registry == null ) @@ -98,6 +106,10 @@ public class DataAccessPointRegistry extends Registry<String, DataAccessPoint> return registry; } + /** + * Set or change the {@link DataAccessPointRegistry}. + * This is atomic. (In Jetty, it is backed by a ConcurrentHashMap). + */ public static void set(ServletContext cxt, DataAccessPointRegistry registry) { cxt.setAttribute(Fuseki.attrNameRegistry, registry); } diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/Dispatcher.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/Dispatcher.java index a93c3cc3ac..8ceac84ea1 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/Dispatcher.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/Dispatcher.java @@ -388,11 +388,11 @@ public class Dispatcher { } else if ( GSP_RW.equals(operation) ) { // If asking for GSP_RW, but only GSP_R is available ... // ... if OPTIONS, use GSP_R. - if ( action.getMethod().equals(HttpNames.METHOD_OPTIONS) && epSet.contains(GSP_R) ) + if ( action.getRequestMethod().equals(HttpNames.METHOD_OPTIONS) && epSet.contains(GSP_R) ) return GSP_R; // ... else 405 if ( epSet.contains(GSP_R) ) - ServletOps.errorMethodNotAllowed(action.getMethod()); + ServletOps.errorMethodNotAllowed(action.getRequestMethod()); } } return operation; diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionLib.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionLib.java index a65dbcba99..8081749642 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionLib.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionLib.java @@ -401,8 +401,6 @@ public class ActionLib { } private static void setCommonHeadersForOptions(HttpServletResponse httpResponse) { - if ( Fuseki.CORS_ENABLED ) - httpResponse.setHeader(HttpNames.hAccessControlAllowHeaders, "X-Requested-With, Content-Type, Authorization"); setCommonHeaders(httpResponse); } @@ -411,8 +409,6 @@ public class ActionLib { } private static void setCommonHeaders(HttpServletResponse httpResponse) { - if ( Fuseki.CORS_ENABLED ) - httpResponse.setHeader(HttpNames.hAccessControlAllowOrigin, "*"); if ( Fuseki.outputFusekiServerHeader ) httpResponse.setHeader(HttpNames.hServer, Fuseki.serverHttpName); } diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionProcessor.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionProcessor.java index caad104ec4..44085722aa 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionProcessor.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionProcessor.java @@ -29,7 +29,7 @@ public interface ActionProcessor { * @param action HTTP Action */ public default void process(HttpAction action) { - switch ( action.getMethod() ) { + switch ( action.getRequestMethod() ) { case METHOD_GET -> execGet(action); case METHOD_POST -> execPost(action); case METHOD_PATCH -> execPatch(action); @@ -38,7 +38,7 @@ public interface ActionProcessor { case METHOD_HEAD -> execHead(action); case METHOD_OPTIONS-> execOptions(action); case METHOD_TRACE -> execTrace(action); - default -> execAny(action.getMethod(), action); + default -> execAny(action.getRequestMethod(), action); } } diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/BaseActionREST.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/BaseActionREST.java index b8ff4c3cdc..2797d09337 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/BaseActionREST.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/BaseActionREST.java @@ -46,6 +46,6 @@ public class BaseActionREST extends ActionREST { public void validate(HttpAction action) { } private void notSupported(HttpAction action) { - ServletOps.errorMethodNotAllowed(action.getMethod()+" "+action.getDatasetName()); + ServletOps.errorMethodNotAllowed(action.getRequestMethod()+" "+action.getDatasetName()); } } diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/HttpAction.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/HttpAction.java index ef3935646e..9e35cd3310 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/HttpAction.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/HttpAction.java @@ -316,6 +316,7 @@ public class HttpAction transactional.begin(txnType); activeDSG = dsg; if ( dataService != null ) + // Paired with finishTxn in end() dataService.startTxn(txnType); startActionTxn(); } @@ -491,6 +492,7 @@ public class HttpAction // ---- public void commit() { +//dataService.finishTxn(); if ( transactional != null ) transactional.commit(); endInternal(); @@ -616,6 +618,8 @@ public class HttpAction // ---- Request - response abstraction. + /** @deprecated Use {@link #getRequestMethod}. */ + @Deprecated(since="5.1.0", forRemoval=true) public String getMethod() { return request.getMethod(); } public HttpServletRequest getRequest() { return request; } diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ServletOps.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ServletOps.java index 74da22ed83..23a09da8a0 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ServletOps.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ServletOps.java @@ -248,11 +248,11 @@ public class ServletOps { } public static void error(int statusCode) { - throw new ActionErrorException(statusCode, null, null); + throw actionErrorException(statusCode, null, null); } public static void error(int statusCode, String string) { - throw new ActionErrorException(statusCode, string, null); + throw actionErrorException(statusCode, string, null); } public static void errorOccurred(String message) { @@ -266,7 +266,12 @@ public class ServletOps { public static void errorOccurred(String message, Throwable ex) { if ( ex instanceof ActionErrorException actionErr ) throw actionErr; - throw new ActionErrorException(HttpSC.INTERNAL_SERVER_ERROR_500, message, ex); + throw actionErrorException(HttpSC.INTERNAL_SERVER_ERROR_500, message, ex); + /* Does not return */ + } + + private static ActionErrorException actionErrorException(int statusCode, String message, Throwable ex) { + return new ActionErrorException(statusCode, message, ex); } public static String formatForLog(String string) { diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/UploadRDF.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/UploadRDF.java index 24de0d7b94..0a9af3cf76 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/UploadRDF.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/UploadRDF.java @@ -54,7 +54,7 @@ public class UploadRDF extends ActionREST { @Override protected void doPatch(HttpAction action) { unsupported(action); } private void unsupported(HttpAction action) { - ServletOps.errorMethodNotAllowed(action.getMethod()); + ServletOps.errorMethodNotAllowed(action.getRequestMethod()); } @Override 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 1c610b5cc2..b1e8cc9098 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 @@ -34,7 +34,7 @@ import org.apache.jena.fuseki.Fuseki; /** * FusekiLogging. * <p> - * This applies to Fuseki run from the command line and embedded. + * This applies to Fuseki run from the command line, as a combined jar and as an embedded server. * <p> * This does not apply to Fuseki running in Tomcat where it uses the * servlet 3.0 mechanism described in @@ -49,10 +49,9 @@ public class FusekiLogging // Set logging. // 1/ Use system property log4j2.configurationFile if defined. - // 2/ Use file:log4j2.properties if exists + // 2/ Use file:log4j2.properties if exists [Jena extension] // 3/ Use log4j2.properties on the classpath. - // 4/ Use org/apache/jena/fuseki/log4j2.properties on the classpath. - // 5/ Use built in string + // 4/ Use built in string /** * Places for the log4j properties file at (3). diff --git a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiLib.java b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiLib.java index 0b0d152525..5a6730639e 100644 --- a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiLib.java +++ b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiLib.java @@ -21,20 +21,24 @@ package org.apache.jena.fuseki.main; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; +import jakarta.servlet.ServletContext; +import org.apache.jena.atlas.logging.Log; +import org.apache.jena.fuseki.Fuseki; import org.apache.jena.fuseki.access.AccessCtl_AllowGET; import org.apache.jena.fuseki.access.AccessCtl_Deny; import org.apache.jena.fuseki.access.AccessCtl_GSP_R; import org.apache.jena.fuseki.access.AccessCtl_SPARQL_QueryDataset; -import org.apache.jena.fuseki.server.DataAccessPointRegistry; -import org.apache.jena.fuseki.server.Endpoint; -import org.apache.jena.fuseki.server.Operation; +import org.apache.jena.fuseki.build.FusekiConfig; +import org.apache.jena.fuseki.server.*; import org.apache.jena.fuseki.servlets.ActionService; import org.apache.jena.fuseki.servlets.GSP_RW; import org.apache.jena.fuseki.servlets.HttpAction; +import org.apache.jena.rdf.model.Model; import org.apache.jena.riot.WebContent; /** Actions on and about a {@link FusekiServer} */ @@ -52,6 +56,34 @@ public class FusekiLib { return names; } + /** + * Process a configuration mode to find the DataServices and reset the server + * {@link DataAccessPointRegistry}. The only server-level setting processed is + * the {@code fuseki:services} list. Other settings are ignored. + */ + public static void reload(FusekiServer server, Model configuration) { + DataAccessPointRegistry newRegistry = new DataAccessPointRegistry(); + OperationRegistry operationRegistry = server.getOperationRegistry(); + + try { + List<DataAccessPoint> newDAPs = FusekiConfig.servicesAndDatasets(configuration.getGraph()); + newDAPs.forEach(dap->newRegistry.register(dap)); + FusekiServer.Builder.prepareDataServices(newRegistry, operationRegistry); + // Reload : switch DataAccessPointRegistry + setDataAccessPointRegistry(server, newRegistry); + } catch (RuntimeException ex) { + Log.error(Fuseki.serverLog, "Failed to load a new configuration", ex); + } + } + + public static void setDataAccessPointRegistry(FusekiServer server, DataAccessPointRegistry newRegistry) { + Objects.requireNonNull(server, "server"); + Objects.requireNonNull(newRegistry, "newRegistry"); + ServletContext cxt = server.getServletContext(); + // This is atomic (in Jetty, it is backed by a ConcurrentHashMap). + DataAccessPointRegistry.set(cxt, newRegistry); + } + /** * Return a {@code FusekiServer.Builder} setup for data access control. */ @@ -82,7 +114,6 @@ public class FusekiLib { public static void modifyForAccessCtl(DataAccessPointRegistry dapRegistry, Function<HttpAction, String> determineUser) { dapRegistry.forEach((name, dap) -> { dap.getDataService().forEachEndpoint(ep->{ - Operation op = ep.getOperation(); modifyForAccessCtl(ep, determineUser); }); }); diff --git a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java index 812d26b1f2..261a10cda7 100644 --- a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java +++ b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java @@ -28,7 +28,6 @@ import java.util.*; import java.util.function.Function; import java.util.function.Predicate; - import jakarta.servlet.Filter; import jakarta.servlet.ServletContext; import jakarta.servlet.http.HttpServlet; @@ -40,6 +39,8 @@ import org.apache.jena.atlas.lib.FileOps; import org.apache.jena.atlas.lib.IRILib; import org.apache.jena.atlas.lib.Pair; import org.apache.jena.atlas.lib.Registry; +import org.apache.jena.atlas.logging.FmtLog; +import org.apache.jena.atlas.logging.Log; import org.apache.jena.atlas.web.AuthScheme; import org.apache.jena.fuseki.Fuseki; import org.apache.jena.fuseki.FusekiConfigException; @@ -57,7 +58,9 @@ import org.apache.jena.fuseki.servlets.*; import org.apache.jena.graph.Graph; import org.apache.jena.graph.Node; import org.apache.jena.query.Dataset; -import org.apache.jena.rdf.model.*; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdf.model.Resource; import org.apache.jena.sparql.core.DatasetGraph; import org.apache.jena.sparql.core.assembler.AssemblerUtils; import org.apache.jena.sparql.util.Context; @@ -162,6 +165,17 @@ public class FusekiServer { return new Builder(serviceDispatchRegistry, context); } + /** + * Return the {@code FusekiServer} associated with a {@link ServletContext}. + * <p> + * This will return null if the {@link ServletContext} is not from a FusekiServer. + */ + public static FusekiServer get(ServletContext cxt) { + FusekiServer server = (FusekiServer)cxt.getAttribute(Fuseki.attrFusekiServer); + if ( server == null ) + Log.warn(Fuseki.serverLog, "No FusekiServer registered in ServletContext"); + return server; + } /** * Default port when running in Java via {@code FusekiServer....build()}. @@ -178,18 +192,24 @@ public class FusekiServer { private int httpsPort; private final String staticContentDir; private final ServletContext servletContext; + private final String configFilename; private final FusekiModules modules; private FusekiServer(int httpPort, int httpsPort, Server server, String staticContentDir, FusekiModules modules, + String configFilename, ServletContext fusekiServletContext) { this.server = Objects.requireNonNull(server); this.httpPort = httpPort; this.httpsPort = httpsPort; this.staticContentDir = staticContentDir; this.servletContext = Objects.requireNonNull(fusekiServletContext); + this.configFilename = configFilename; this.modules = Objects.requireNonNull(modules); + + // So servlets can find the server. + servletContext.setAttribute(Fuseki.attrFusekiServer, this); } /** @@ -317,6 +337,14 @@ public class FusekiServer { return modules; } + /** + * Return the filename of the configuration file. + * Returns null if configuration was by APIs and there is no such file was used. + */ + public String getConfigFilename() { + return configFilename; + } + /** * Start the server - the server continues to run after this call returns. * To synchronise with the server stopping, call {@link #join}. @@ -426,6 +454,9 @@ public class FusekiServer { private boolean withTasks = false; private String jettyServerConfig = null; + + // Record calls to parseConfigFile. + private String configFileName = null; private Model configModel = null; private Map<String, String> corsInitParams = null; @@ -796,12 +827,34 @@ public class FusekiServer { /** * Configure using a Fuseki services/datasets assembler file. * <p> - * The application is responsible for ensuring a correct classpath. For example, - * including a dependency on {@code jena-text} if the configuration file includes - * a text index. + * The configuration file is processed in this call so that subsequent + * builder operations can refer to data services created by this call. + * <p> + * The calling application is responsible for ensuring a correct classpath. + * For example, including a dependency on {@code jena-text} if the + * configuration file includes a text index. + */ + public Builder parseConfigFile(Path filename) { + requireNonNull(filename, "filename"); + parseConfigFile(filename.toString()); + return this; + } + + /** + * Configure using a Fuseki services/datasets assembler file. + * <p> + * The configuration file is processed in this call so that subsequent + * builder operations can refer to data services created by this call. + * <p> + * The calling application is responsible for ensuring a correct classpath. + * For example, including a dependency on {@code jena-text} if the + * configuration file includes a text index. */ public Builder parseConfigFile(String filename) { requireNonNull(filename, "filename"); + if ( configFileName != null ) + FmtLog.warn(Fuseki.configLog, "Multiple configuration files (reload will not be possible)"); + this.configFileName = filename; Model model = AssemblerUtils.readAssemblerFile(filename); parseConfig(model); return this; @@ -1374,9 +1427,16 @@ public class FusekiServer { if ( hasFusekiSecurityHandler ) applyAccessControl(handler, dapRegistry); + + if ( jettyServerConfig != null ) { + // Jetty server configuration provided. Server server = jettyServer(handler, jettyServerConfig); - return new FusekiServer(-1, -1, server, staticContentDir, modules, handler.getServletContext()); + return new FusekiServer(-1, -1, server, + staticContentDir, + modules, + configFileName, + handler.getServletContext()); } Server server; @@ -1401,7 +1461,11 @@ public class FusekiServer { if ( networkLoopback ) applyLocalhost(server); - FusekiServer fusekiServer = new FusekiServer(httpPort, httpsPort, server, staticContentDir, modules, handler.getServletContext()); + FusekiServer fusekiServer = new FusekiServer(httpPort, httpsPort, server, + staticContentDir, + modules, + configFileName, + handler.getServletContext()); FusekiModuleStep.server(fusekiServer); return fusekiServer; } finally { @@ -1442,7 +1506,7 @@ public class FusekiServer { return handler; } - private static void prepareDataServices(DataAccessPointRegistry dapRegistry, OperationRegistry operationReg) { + /*package*/ static void prepareDataServices(DataAccessPointRegistry dapRegistry, OperationRegistry operationReg) { dapRegistry.forEach((name, dap) -> { // Override for graph-level access control. if ( DataAccessCtl.isAccessControlled(dap.getDataService().getDataset()) ) { diff --git a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/JettyServer.java b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/JettyServer.java index 80641c76f6..fb65a1044f 100644 --- a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/JettyServer.java +++ b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/JettyServer.java @@ -393,6 +393,7 @@ public class JettyServer { try { Fuseki.setVerbose(cxt, verbose); OperationRegistry.set(cxt, OperationRegistry.createEmpty()); + // Empty DataAccessPointRegistry, no nulls! DataAccessPointRegistry.set(cxt, new DataAccessPointRegistry()); } catch (NoClassDefFoundError err) { LOG.info("Fuseki classes not found"); diff --git a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiModuleStep.java b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiModuleStep.java index 7735c187b4..1a320f5d21 100644 --- a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiModuleStep.java +++ b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/sys/FusekiModuleStep.java @@ -77,7 +77,7 @@ public class FusekiModuleStep { } /** - * Sever reload. + * Server reload. * Return true if reload happened, else false. * @see FusekiBuildCycle#serverConfirmReload * @see FusekiBuildCycle#serverReload diff --git a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/CustomTestService.java b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/CustomTestService.java index a7b8cdbf18..4fc40a60c2 100644 --- a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/CustomTestService.java +++ b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/CustomTestService.java @@ -76,6 +76,6 @@ public class CustomTestService extends ActionREST { public void validate(HttpAction action) { } private void notSupported(HttpAction action) { - ServletOps.errorMethodNotAllowed(action.getMethod()+" "+action.getDatasetName()); + ServletOps.errorMethodNotAllowed(action.getRequestMethod()+" "+action.getDatasetName()); } } diff --git a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TS_FusekiMain.java b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TS_FusekiMain.java index e0fa22d5e8..92348e10d1 100644 --- a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TS_FusekiMain.java +++ b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TS_FusekiMain.java @@ -18,11 +18,12 @@ package org.apache.jena.fuseki.main; -import org.apache.jena.fuseki.main.prefixes.*; -import org.apache.jena.fuseki.main.sys.TestFusekiModules; import org.junit.platform.suite.api.SelectClasses; import org.junit.platform.suite.api.Suite; +import org.apache.jena.fuseki.main.prefixes.PrefixesServiceTests; +import org.apache.jena.fuseki.main.sys.TestFusekiModules; + @Suite @SelectClasses({ @@ -55,9 +56,9 @@ import org.junit.platform.suite.api.Suite; , TestFusekiCustomScriptFunc.class , PrefixesServiceTests.class - , TestMetrics.class , TestFusekiShaclValidation.class + }) public class TS_FusekiMain {} diff --git a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiServerBuild.java b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiServerBuild.java index cefa3ae31d..611ca3a80e 100644 --- a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiServerBuild.java +++ b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiServerBuild.java @@ -27,6 +27,7 @@ import static org.junit.Assert.*; import java.io.IOException; import java.util.function.Consumer; +import jakarta.servlet.ServletContext; import org.apache.jena.atlas.iterator.Iter; import org.apache.jena.atlas.logging.Log; import org.apache.jena.atlas.logging.LogCtl; @@ -60,9 +61,13 @@ public class TestFusekiServerBuild { @Test public void fuseki_build_1() { FusekiServer server = FusekiServer.create().port(3456).build(); - // Not started. Port not assigned. assertTrue(server.getHttpPort() == 3456 ); assertTrue(server.getHttpsPort() == -1 ); + + ServletContext cxt = server.getServletContext(); + FusekiServer server2 = FusekiServer.get(cxt); + assertNotNull(server2); + assertEquals(server, server2); } @Test public void fuseki_build_2() { diff --git a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestPlainServer.java b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestPlainServer.java index 7aec058fa8..424cda1007 100644 --- a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestPlainServer.java +++ b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestPlainServer.java @@ -109,7 +109,7 @@ public class TestPlainServer { } @Test public void plainFile3() { - String x = HttpOp.httpGetString(serverURL+"/file-top.txt"); + String x = HttpOp.httpGetString(serverURL+"/exists.txt"); assertNotNull(x); assertTrue(x.contains("CONTENT")); } diff --git a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/examples/DemoService.java b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/examples/DemoService.java index b83273b764..6db9b378ca 100644 --- a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/examples/DemoService.java +++ b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/examples/DemoService.java @@ -79,6 +79,6 @@ public class DemoService extends ActionREST { public void validate(HttpAction action) { } private void notSupported(HttpAction action) { - ServletOps.errorMethodNotAllowed(action.getMethod()+" "+action.getActionURI()); + ServletOps.errorMethodNotAllowed(action.getRequestMethod()+" "+action.getActionURI()); } } diff --git a/jena-fuseki2/jena-fuseki-main/testing/Config/reload-config1.ttl b/jena-fuseki2/jena-fuseki-main/testing/Config/reload-config1.ttl new file mode 100644 index 0000000000..3e916dbfe8 --- /dev/null +++ b/jena-fuseki2/jena-fuseki-main/testing/Config/reload-config1.ttl @@ -0,0 +1,16 @@ +PREFIX : <#> +PREFIX fuseki: <http://jena.apache.org/fuseki#> +PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> +PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> +PREFIX ja: <http://jena.hpl.hp.com/2005/11/Assembler#> + +:service rdf:type fuseki:Service ; + fuseki:name "ds" ; + fuseki:endpoint [ fuseki:operation fuseki:query ; ] ; + fuseki:endpoint [ fuseki:operation fuseki:update ; ] ; + fuseki:endpoint [ fuseki:operation fuseki:gsp-rw ; ] ; + fuseki:endpoint [ fuseki:operation fuseki:patch ; ] ; + fuseki:dataset :dataset ; +. + +:dataset rdf:type ja:MemoryDataset . diff --git a/jena-fuseki2/jena-fuseki-main/testing/Config/reload-config2.ttl b/jena-fuseki2/jena-fuseki-main/testing/Config/reload-config2.ttl new file mode 100644 index 0000000000..bad6403a74 --- /dev/null +++ b/jena-fuseki2/jena-fuseki-main/testing/Config/reload-config2.ttl @@ -0,0 +1,25 @@ +PREFIX : <#> +PREFIX fuseki: <http://jena.apache.org/fuseki#> +PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> +PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> +PREFIX ja: <http://jena.hpl.hp.com/2005/11/Assembler#> + +:service rdf:type fuseki:Service ; + fuseki:name "dataset2" ; + fuseki:endpoint [ fuseki:operation fuseki:query ; ] ; + fuseki:endpoint [ fuseki:operation fuseki:update ; ] ; + fuseki:endpoint [ fuseki:operation fuseki:gsp-rw ; ] ; + fuseki:endpoint [ fuseki:operation fuseki:patch ; ] ; + fuseki:dataset :dataset ; +. + +:dataset rdf:type ja:MemoryDataset ; + #ja:data "data.trig"; +. + +:service2 rdf:type fuseki:Service ; + fuseki:name "null" ; + ## No operations. + ## Always empty dataset. + fuseki:dataset [ rdf:type ja:RDFDatasetZero ] ; + . diff --git a/jena-fuseki2/jena-fuseki-main/testing/Files/exists.txt b/jena-fuseki2/jena-fuseki-main/testing/Files/exists.txt new file mode 100644 index 0000000000..4c5132ec87 --- /dev/null +++ b/jena-fuseki2/jena-fuseki-main/testing/Files/exists.txt @@ -0,0 +1,2 @@ +This file is used by the plain server static resource tests. +CONTENT diff --git a/jena-fuseki2/jena-fuseki-main/testing/Files/file-top.txt b/jena-fuseki2/jena-fuseki-main/testing/Files/file-top.txt deleted file mode 100644 index a980c835bc..0000000000 --- a/jena-fuseki2/jena-fuseki-main/testing/Files/file-top.txt +++ /dev/null @@ -1,2 +0,0 @@ -# Licensed under the terms of http://www.apache.org/licenses/LICENSE-2.0 -CONTENT
