/*
* 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.sparql.modify;
import java.util.ArrayList ;
import java.util.List ;
import java.util.Map ;
import org.apache.http.client.HttpClient;
import org.apache.http.protocol.HttpContext ;
import org.apache.jena.riot.web.HttpOp ;
import org.apache.jena.sparql.core.DatasetGraph ;
import org.apache.jena.sparql.engine.http.HttpParams ;
import org.apache.jena.sparql.engine.http.Params ;
import org.apache.jena.sparql.engine.http.QueryEngineHTTP ;
import org.apache.jena.sparql.engine.http.Service ;
import org.apache.jena.sparql.util.Context ;
import org.apache.jena.sparql.util.Symbol ;
import org.apache.jena.update.UpdateProcessor ;
import org.apache.jena.update.UpdateRequest ;
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
/**
* Abstract base class for update processors that perform remote updates over
* HTTP
*
*/
public abstract class UpdateProcessRemoteBase implements UpdateProcessor {
private static Logger log = LoggerFactory.getLogger(UpdateProcessRemoteBase.class);
/**
* Symbol used to set a {@link HttpContext} which will be used for HTTP
* requests
*/
public static final Symbol HTTP_CONTEXT = Symbol.create("httpContext");
private final UpdateRequest request;
private final String endpoint;
private final Context context;
private HttpClient client;
private Params params;
protected List<String> defaultGraphURIs = new ArrayList<>();
protected List<String> namedGraphURIs = new ArrayList<>();
/**
* Creates a new remote update processor
*
* @param request
* Update request
* @param endpoint
* Update endpoint
* @param context
* Context
*/
public UpdateProcessRemoteBase(UpdateRequest request, String endpoint, Context context) {
super();
this.request = request;
this.endpoint = endpoint;
this.context = Context.setupContext(context, null);
// Apply service configuration if applicable
UpdateProcessRemoteBase.applyServiceConfig(endpoint, this);
}
/**
* <p>
* Helper method which applies configuration from the Context to the query
* engine if a service context exists for the given URI
* </p>
* <p>
* Based off proposed patch for JENA-405 but modified to apply all relevant
* configuration, this is in part also based off of the private
* {@code configureQuery()} method of the {@link Service} class though it
* omits parameter merging since that will be done automatically whenever
* the {@link QueryEngineHTTP} instance makes a query for remote submission.
* </p>
*
* @param serviceURI
* Service URI
*/
private static void applyServiceConfig(String serviceURI, UpdateProcessRemoteBase engine) {
@SuppressWarnings("unchecked")
Map<String, Context> serviceContextMap = (Map<String, Context>) engine.context.get(Service.serviceContext);
if (serviceContextMap != null && serviceContextMap.containsKey(serviceURI)) {
Context serviceContext = serviceContextMap.get(serviceURI);
if (log.isDebugEnabled())
log.debug("Endpoint URI {} has SERVICE Context: {} ", serviceURI, serviceContext);
// Apply client settings
HttpClient client = serviceContext.get(Service.queryClient);
if (client != null) {
if (log.isDebugEnabled())
log.debug("Using context-supplied client for endpoint URI {}", serviceURI);
engine.setClient(client);
}
}
}
@Override
public DatasetGraph getDatasetGraph() {
return null;
}
/**
* Gets the endpoint
*
* @return Endpoint URI
*/
public String getEndpoint() {
return this.endpoint;
}
/**
* Gets the generated HTTP query string portion of the endpoint URL if applicable
* <p>
* Generated string will not include leading ? so that consuming code can
* decide whether to add this themselves since the generated query string
* may be being used in addition to an existing query string.
* </p>
*
* @return Generated query string
*/
public String getQueryString() {
return this.getParams().httpString();
}
/**
* Gets the parameters for the execution
*
* @return Parameters
*/
public Params getParams() {
Params ps = this.params != null ? new Params(this.params) : new Params();
if (this.defaultGraphURIs != null) {
for (String defaultGraph : this.defaultGraphURIs) {
ps.addParam(HttpParams.pUsingGraph, defaultGraph);
}
}
if (this.namedGraphURIs != null) {
for (String namedGraph : this.namedGraphURIs) {
ps.addParam(HttpParams.pUsingNamedGraph, namedGraph);
}
}
return ps;
}
/**
* Gets the update request
*
* @return Update request
*/
public UpdateRequest getUpdateRequest() {
return this.request;
}
/**
* Adds a default graph
*
* @param defaultGraph
* Default Graph URI
*/
public void addDefaultGraph(String defaultGraph) {
if (this.defaultGraphURIs == null) {
this.defaultGraphURIs = new ArrayList<>();
}
this.defaultGraphURIs.add(defaultGraph);
}
/**
* Adds a named graph
*
* @param namedGraph
* Named Graph URi
*/
public void addNamedGraph(String namedGraph) {
if (this.namedGraphURIs == null) {
this.namedGraphURIs = new ArrayList<>();
}
this.namedGraphURIs.add(namedGraph);
}
/**
* Adds a custom parameter to the request
*
* @param field
* Field
* @param value
* Value
*/
public void addParam(String field, String value) {
if (this.params == null)
this.params = new Params();
this.params.addParam(field, value);
}
/**
* Sets the default graphs
*
* @param defaultGraphs
* Default Graphs
*/
public void setDefaultGraphs(List<String> defaultGraphs) {
this.defaultGraphURIs = defaultGraphs;
}
/**
* Sets the named graphs
*
* @param namedGraphs
* Named Graphs
*/
public void setNamedGraphs(List<String> namedGraphs) {
this.namedGraphURIs = namedGraphs;
}
/**
* Convenience method to set the {@link HttpContext}
*
* @param httpContext
* HTTP Context
*/
public void setHttpContext(HttpContext httpContext) {
getContext().put(HTTP_CONTEXT, httpContext);
}
/**
* Convenience method to get the {@link HttpContext}
*
* @return HttpContext
*/
public HttpContext getHttpContext() {
return (HttpContext) getContext().get(HTTP_CONTEXT);
}
@Override
public Context getContext() {
return context;
}
/**
* Sets the client to use
* <p>
* Note that you can globally set an client via
* {@link HttpOp#setDefaultHttpClient(HttpClient)} to avoid the
* need to set client on a per-request basis
* </p>
*
* @param client
* HTTP client
*/
public void setClient(HttpClient client) {
this.client = client;
}
/**
* Gets the client that has been set (if any)
* <p>
* If no client is used then the default client will be used,
* this can be configured via the
* {@link HttpOp#setDefaultHttpClient(HttpClient)} method.
* </p>
*
* @return HTTP client if set, null otherwise
*/
public HttpClient getClient() {
return this.client;
}
}