Skip to content

Commit

Permalink
apacheGH-2462: Reload action for configuration files
Browse files Browse the repository at this point in the history
  • Loading branch information
afs committed May 18, 2024
1 parent 51caaf2 commit afd2b67
Show file tree
Hide file tree
Showing 28 changed files with 466 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,37 +40,33 @@

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>
Expand Down Expand Up @@ -175,6 +171,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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ public static List<DataAccessPoint> servicesAndDatasets(Model model) {
NamedDatasetAssembler.sharedDatasetPool.clear();
// ---- Services
// Server to services.
ResultSet rs = BuildLib.query("SELECT * { ?s fu:services [ list:member ?service ] }", model, "s", server);
ResultSet rs = BuildLib.query("SELECT ?service { ?s fu:services [ list:member ?service ] }", model, "s", server);
List<DataAccessPoint> accessPoints = new ArrayList<>();

// If none, look for services by type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.apache.jena.fuseki.Fuseki;
import org.apache.jena.fuseki.FusekiException;
import org.apache.jena.fuseki.metrics.MetricsProviderRegistry;
import org.apache.jena.fuseki.servlets.HttpAction;

/**
* Registry of (dataset name, {@link DataAccessPoint}).
Expand Down Expand Up @@ -66,7 +67,7 @@ public void register(DataAccessPoint accessPt) {
*/
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;
}
Expand Down Expand Up @@ -97,14 +98,25 @@ public void print(String string) {
});
}

// 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 )
Log.warn(DataAccessPointRegistry.class, "No data access point registry for ServletContext");
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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -388,11 +388,11 @@ private static Operation mapGSP(HttpAction action, Operation operation, Endpoint
} 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,8 +401,6 @@ public static void setCommonHeadersForOptions(HttpAction action) {
}

private static void setCommonHeadersForOptions(HttpServletResponse httpResponse) {
if ( Fuseki.CORS_ENABLED )
httpResponse.setHeader(HttpNames.hAccessControlAllowHeaders, "X-Requested-With, Content-Type, Authorization");
setCommonHeaders(httpResponse);
}

Expand All @@ -411,8 +409,6 @@ public static void setCommonHeaders(HttpAction action) {
}

private static void setCommonHeaders(HttpServletResponse httpResponse) {
if ( Fuseki.CORS_ENABLED )
httpResponse.setHeader(HttpNames.hAccessControlAllowOrigin, "*");
if ( Fuseki.outputFusekiServerHeader )
httpResponse.setHeader(HttpNames.hServer, Fuseki.serverHttpName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -38,7 +38,7 @@ public default void process(HttpAction action) {
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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ public void begin(TxnType txnType) {
transactional.begin(txnType);
activeDSG = dsg;
if ( dataService != null )
// Paired with finishTxn in end()
dataService.startTxn(txnType);
}

Expand Down Expand Up @@ -332,7 +333,8 @@ private void endOfAction() {
}

public void commit() {
dataService.finishTxn();
// Signalling the end of transaction is done in end().
//dataService.finishTxn();
transactional.commit();
end();
}
Expand Down Expand Up @@ -462,6 +464,8 @@ public String toString() {

// ---- 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; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,11 +252,11 @@ public static void errorForbidden(String msg) {
}

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) {
Expand All @@ -268,11 +268,14 @@ public static void errorOccurred(Throwable ex) {
}

public static void errorOccurred(String message, Throwable ex) {
if ( message == null )
System.err.println();
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
/**
* 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
Expand All @@ -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).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,24 @@
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} */
Expand All @@ -52,6 +56,34 @@ public static Collection<String> names(FusekiServer server) {
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);
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.
*/
Expand Down Expand Up @@ -82,7 +114,6 @@ public static FusekiServer.Builder fusekiBuilderAccessCtl(FusekiServer.Builder b
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);
});
});
Expand Down
Loading

0 comments on commit afd2b67

Please sign in to comment.