blob: f1e7c01c0104bf31af72bfdcbba05733cb98bd93 [file] [log] [blame]
/*
* 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.server;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.ServletContext;
import org.apache.jena.atlas.logging.Log;
import org.apache.jena.fuseki.Fuseki;
import org.apache.jena.fuseki.servlets.*;
import org.apache.jena.riot.WebContent;
/**
* Registry of operations.
*
* The registry (accessed via the servlet context) provides
* <ul>
* <li>Content-type to {@code Operation} via {@link #findByContentType(String)}.
* <li>{@code Operation} to {@link ActionService} implementation via {@link #findHandler(Operation)}
* </ul>
*/
public class OperationRegistry {
// Standard, non-graph-level access control versions.
private static final ActionService queryServlet = new SPARQL_QueryDataset();
private static final ActionService updateServlet = new SPARQL_Update();
private static final ActionService uploadServlet = new SPARQL_Upload();
private static final ActionService gspServlet_R = new GSP_R();
private static final ActionService gspServlet_RW = new GSP_RW();
private static final ActionService noOperation = new NoOpActionService();
private static final ActionService shaclValidation = new SHACL_Validation();
/** The server-wide standard configuration. */
private static final OperationRegistry stdConfig = stdConfig();
/**
* Return the current server-wide standard configuration. It is copied into each
* new FusekiServer created. Changing it after a server has been created does not
* affect the server.
*/
public static OperationRegistry get() { return stdConfig; }
private static OperationRegistry stdConfig() {
OperationRegistry stdOpReg = new OperationRegistry();
stdOpReg.register(Operation.Query, WebContent.contentTypeSPARQLQuery, queryServlet);
stdOpReg.register(Operation.Update, WebContent.contentTypeSPARQLUpdate, updateServlet);
stdOpReg.register(Operation.Upload, null, uploadServlet);
stdOpReg.register(Operation.Shacl, null, shaclValidation);
stdOpReg.register(Operation.GSP_R, null, gspServlet_R);
stdOpReg.register(Operation.GSP_RW, null, gspServlet_RW);
stdOpReg.register(Operation.NoOp, null, noOperation);
return stdOpReg;
}
/** Copy the configuration from {@code src} into {@code dst}. */
public static void copyConfig(OperationRegistry src, OperationRegistry dst) {
dst.contentTypeToOperation.putAll(src.contentTypeToOperation);
dst.operationToHandler.putAll(src.operationToHandler);
}
/** Map ContentType (lowercase, no charset) to the {@code Operation} for handling it. */
private final Map<String, Operation> contentTypeToOperation = new ConcurrentHashMap<>();
/** Map {@link Operation} to servlet handler.
* {@code Operation}s are the internal symbol identifying an operation,
* not the name used in the configuration file,
* which is mapped by {@link DataService#getEndpoint(String)}.
*/
private final Map<Operation, ActionService> operationToHandler = new ConcurrentHashMap<>();
public OperationRegistry(OperationRegistry other) {
copyConfig(other, this);
}
/** Create a {@code OperationRegistry} with the standard operations included. */
public static OperationRegistry createStd() {
OperationRegistry registry = new OperationRegistry();
copyConfig(stdConfig, registry);
return registry;
}
/** Create an empty {@code OperationRegistry}. */
public static OperationRegistry createEmpty() {
return new OperationRegistry();
}
private OperationRegistry() { }
// public Map<String, Operation> contentTypeToOperation() { return contentTypeToOperation; }
//
// public Map<Operation, ActionService> operationToHandler() { return operationToHandler; }
/** Find the {@link Operation} for a {@code Content-Type}, or return null. */
public Operation findByContentType(String contentType) {
if ( contentType == null )
return null;
return contentTypeToOperation.get(contentType);
}
/** Find the {@link ActionService} implementation for an {@link Operation}, or return null..*/
public ActionService findHandler(Operation operation) {
if ( operation == null )
return null;
return operationToHandler.get(operation);
}
public boolean isRegistered(Operation operation) {
return operationToHandler.containsKey(operation);
}
/**
* Register a new {@link Operation} and the implementation handler.
* <p>
* The application needs to enable an operation on a service endpoint.
* <p>
* Replaces any existing registration.
*/
public void register(Operation operation, ActionService action) {
Objects.requireNonNull(operation);
Objects.requireNonNull(action);
register(operation, null, action);
}
/**
* Register a new {@link Operation}, with its {@code Content-Type} (may be null,
* meaning no dispatch by content type), and the implementation handler.
* <p>
* The application needs to enable an operation on a service endpoint.
* <p>
* Replaces any existing registration.
*/
public void register(Operation operation, String contentType, ActionService action) {
Objects.requireNonNull(operation);
Objects.requireNonNull(action);
if ( contentType != null )
contentTypeToOperation.put(contentType, operation);
else
// Remove any mapping.
contentTypeToOperation.values().remove(operation);
operationToHandler.put(operation, action);
}
/**
* Remove the registration for an operation.
*/
public void unregister(Operation operation) {
Objects.requireNonNull(operation);
operationToHandler.remove(operation);
contentTypeToOperation.values().remove(operation);
}
// The server DataAccessPointRegistry is held in the ServletContext for the server.
public static OperationRegistry get(ServletContext servletContext) {
OperationRegistry registry = (OperationRegistry)servletContext.getAttribute(Fuseki.attrOperationRegistry);
if ( registry == null )
Log.warn(OperationRegistry.class, "No service registry for ServletContext");
return registry;
}
public static void set(ServletContext cxt, OperationRegistry registry) {
cxt.setAttribute(Fuseki.attrOperationRegistry, registry);
}
}