/*
* 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.servlets;
import static org.apache.jena.fuseki.server.CounterName.Requests ;
import static org.apache.jena.fuseki.server.CounterName.RequestsBad ;
import static org.apache.jena.fuseki.server.CounterName.RequestsGood ;
import java.io.InputStream ;
import org.apache.jena.atlas.RuntimeIOException ;
import org.apache.jena.fuseki.Fuseki ;
import org.apache.jena.fuseki.server.* ;
import org.apache.jena.query.QueryCancelledException ;
import org.apache.jena.riot.Lang ;
import org.apache.jena.riot.RDFDataMgr ;
import org.apache.jena.riot.ReaderRIOT ;
import org.apache.jena.riot.RiotException ;
import org.apache.jena.riot.system.ErrorHandler ;
import org.apache.jena.riot.system.ErrorHandlerFactory ;
import org.apache.jena.riot.system.StreamRDF ;
/** SPARQL request lifecycle */
public abstract class ActionSPARQL extends ActionBase
{
private static final long serialVersionUID = 8655400764034493574L;
protected ActionSPARQL() { super(Fuseki.actionLog) ; }
protected abstract void validate(HttpAction action) ;
protected abstract void perform(HttpAction action) ;
/**
* Executes common tasks, including mapping the request to the right dataset, setting the dataset into the HTTP
* action, and retrieving the service for the dataset requested. Finally, it calls the
* {@link #executeAction(HttpAction)} method, which executes the HTTP Action life cycle.
* @param action HTTP Action
*/
@Override
protected void execCommonWorker(HttpAction action) {
DataAccessPoint dataAccessPoint ;
DataService dSrv ;
String datasetUri = mapRequestToDataset(action) ;
if ( datasetUri != null ) {
dataAccessPoint = action.getDataAccessPointRegistry().get(datasetUri) ;
if ( dataAccessPoint == null ) {
ServletOps.errorNotFound("No dataset for URI: "+datasetUri) ;
return ;
}
//dataAccessPoint.
dSrv = dataAccessPoint.getDataService() ;
if ( ! dSrv.isAcceptingRequests() ) {
ServletOps.errorNotFound("Dataset not active: "+datasetUri) ;
return ;
}
} else {
dataAccessPoint = null ;
dSrv = DataService.serviceOnlyDataService() ;
}
String operationName = mapRequestToOperation(action, dataAccessPoint) ;
action.setRequest(dataAccessPoint, dSrv) ;
//operationName = ""
Endpoint op = dSrv.getOperation(operationName) ;
action.setEndpoint(op, operationName);
executeAction(action) ;
}
/** Execute a SPARQL request. Statistics have not been adjusted at this point.
*
* @param action
*/
protected void executeAction(HttpAction action) {
executeLifecycle(action) ;
}
/**
* Standard execution lifecycle for a SPARQL Request.
* <ul>
* <li>{@link #startRequest(HttpAction)}</li>
* <li>initial statistics,</li>
* <li>{@link #validate(HttpAction)} request,</li>
* <li>{@link #perform(HttpAction)} request,</li>
* <li>completion/error statistics,</li>
* <li>{@link #finishRequest(HttpAction)}
* </ul>
*
* @param action
*/
// This is the service request lifecycle.
final
protected void executeLifecycle(HttpAction action) {
startRequest(action) ;
// And also HTTP counter
CounterSet csService = action.getDataService().getCounters() ;
CounterSet csOperation = null ;
if ( action.getEndpoint() != null )
// Direct naming GSP does not have an "endpoint".
csOperation = action.getEndpoint().getCounters() ;
incCounter(csService, Requests) ;
incCounter(csOperation, Requests) ;
try {
// Either exit this via "bad request" on validation
// or in execution in perform.
try {
validate(action) ;
} catch (ActionErrorException ex) {
incCounter(csOperation, RequestsBad) ;
incCounter(csService, RequestsBad) ;
throw ex ;
}
try {
perform(action) ;
// Success
incCounter(csOperation, RequestsGood) ;
incCounter(csService, RequestsGood) ;
} catch (ActionErrorException | QueryCancelledException | RuntimeIOException ex) {
incCounter(csOperation, RequestsBad) ;
incCounter(csService, RequestsBad) ;
throw ex ;
}
} finally {
finishRequest(action) ;
}
}
/**
* Map request {@link HttpAction} to uri in the registry.
* A return of {@code null} means no mapping done (passthrough).
* @param uri the URI
* @return the dataset
*/
protected String mapRequestToDataset(HttpAction action) {
return ActionLib.mapRequestToDataset(action) ;
}
/**
* Map request to uri in the registry. {@code null} means no mapping done
* (passthrough).
*/
protected String mapRequestToOperation(HttpAction action, DataAccessPoint dataAccessPoint) {
return ActionLib.mapRequestToOperation(action, dataAccessPoint) ;
}
/** Increment counter */
protected static void incCounter(Counters counters, CounterName name) {
if ( counters == null ) return ;
incCounter(counters.getCounters(), name) ;
}
/** Decrement counter */
protected static void decCounter(Counters counters, CounterName name) {
if ( counters == null ) return ;
decCounter(counters.getCounters(), name) ;
}
protected static void incCounter(CounterSet counters, CounterName name) {
if ( counters == null )
return ;
try {
if ( counters.contains(name) )
counters.inc(name) ;
} catch (Exception ex) {
Fuseki.serverLog.warn("Exception on counter inc", ex) ;
}
}
protected static void decCounter(CounterSet counters, CounterName name) {
if ( counters == null )
return ;
try {
if ( counters.contains(name) )
counters.dec(name) ;
} catch (Exception ex) {
Fuseki.serverLog.warn("Exception on counter dec", ex) ;
}
}
public static void parse(HttpAction action, StreamRDF dest, InputStream input, Lang lang, String base) {
try {
ReaderRIOT r = RDFDataMgr.createReader(lang) ;
if ( r == null )
ServletOps.errorBadRequest("No parser for language '"+lang.getName()+"'") ;
ErrorHandler errorHandler = ErrorHandlerFactory.errorHandlerStd(action.log);
r.setErrorHandler(errorHandler);
r.read(input, base, null, dest, null) ;
}
catch (RiotException ex) { ServletOps.errorBadRequest("Parse error: "+ex.getMessage()) ; }
}
}