VIVO-1025 Improvements to the Virtuoso driver.
This commit is contained in:
parent
586fa09666
commit
0b79dfdf01
5 changed files with 179 additions and 45 deletions
|
@ -27,6 +27,7 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet;
|
||||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
|
||||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
|
||||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils;
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString;
|
||||||
|
|
||||||
public class RDFServiceGraphBulkUpdater implements BulkUpdateHandler {
|
public class RDFServiceGraphBulkUpdater implements BulkUpdateHandler {
|
||||||
private static final Log log = LogFactory.getLog(RDFServiceGraphBulkUpdater.class);
|
private static final Log log = LogFactory.getLog(RDFServiceGraphBulkUpdater.class);
|
||||||
|
@ -203,7 +204,8 @@ public class RDFServiceGraphBulkUpdater implements BulkUpdateHandler {
|
||||||
|
|
||||||
private static void removeAll(Graph g, Node s, Node p, Node o)
|
private static void removeAll(Graph g, Node s, Node p, Node o)
|
||||||
{
|
{
|
||||||
log.debug("removeAll: g=" + g + ", s=" + s + ", p=" + p + ", o=" + o);
|
log.debug("removeAll: g=" + ToString.graphToString(g) + ", s=" + s
|
||||||
|
+ ", p=" + p + ", o=" + o);
|
||||||
if (!(g instanceof RDFServiceGraph)) {
|
if (!(g instanceof RDFServiceGraph)) {
|
||||||
removeAllTripleByTriple(g, s, p, o);
|
removeAllTripleByTriple(g, s, p, o);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -70,7 +70,7 @@ public class RDFServiceSparql extends RDFServiceImpl implements RDFService {
|
||||||
private static final Log log = LogFactory.getLog(RDFServiceImpl.class);
|
private static final Log log = LogFactory.getLog(RDFServiceImpl.class);
|
||||||
protected String readEndpointURI;
|
protected String readEndpointURI;
|
||||||
protected String updateEndpointURI;
|
protected String updateEndpointURI;
|
||||||
private CloseableHttpClient httpClient;
|
protected CloseableHttpClient httpClient;
|
||||||
// the number of triples to be
|
// the number of triples to be
|
||||||
private static final int CHUNK_SIZE = 1000; // added/removed in a single
|
private static final int CHUNK_SIZE = 1000; // added/removed in a single
|
||||||
// SPARQL UPDATE
|
// SPARQL UPDATE
|
||||||
|
@ -467,7 +467,7 @@ public class RDFServiceSparql extends RDFServiceImpl implements RDFService {
|
||||||
try {
|
try {
|
||||||
int statusCode = response.getStatusLine().getStatusCode();
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
if (statusCode > 399) {
|
if (statusCode > 399) {
|
||||||
log.error("response " + statusCode + " to update. \n");
|
log.error("response " + response.getStatusLine() + " to update. \n");
|
||||||
//log.debug("update string: \n" + updateString);
|
//log.debug("update string: \n" + updateString);
|
||||||
throw new RDFServiceException("Unable to perform SPARQL UPDATE");
|
throw new RDFServiceException("Unable to perform SPARQL UPDATE");
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,41 +7,132 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.apache.http.auth.AuthScope;
|
||||||
|
import org.apache.http.auth.UsernamePasswordCredentials;
|
||||||
|
import org.apache.http.client.CredentialsProvider;
|
||||||
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.client.protocol.HttpClientContext;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.BasicCredentialsProvider;
|
||||||
|
|
||||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
|
||||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.sparql.RDFServiceSparql;
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.sparql.RDFServiceSparql;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For now, at least, it is just like an RDFServiceSparql except for:
|
* For now, at least, it is just like an RDFServiceSparql except:
|
||||||
*
|
*
|
||||||
* A small change in the syntax of an UPDATE request.
|
* A username and password are required. These should refer to a Virtuoso user
|
||||||
|
* that posesses the SPARQL_UPDATE role.
|
||||||
|
*
|
||||||
|
* The endpoint URI and the update endpoint URI are derived from the base URI.
|
||||||
|
* You provide: http://localhost:8890
|
||||||
|
* endpoint is: http://localhost:8890/sparql/
|
||||||
|
* update is: http://localhost:8890/DAV/home/username/rdf_sink/vitro_update
|
||||||
|
*
|
||||||
|
* A change in the syntax of an UPDATE request: "INSERT DATA" becomes "INSERT".
|
||||||
|
* This fixes a problem with inserting blank nodes.
|
||||||
|
*
|
||||||
|
* The HTTP request is equipped with the username and password, to answer a
|
||||||
|
* challenge for basic authentication.
|
||||||
*
|
*
|
||||||
* Allow for the nonNegativeInteger bug when checking to see whether a graph has
|
* Allow for the nonNegativeInteger bug when checking to see whether a graph has
|
||||||
* changed.
|
* changed.
|
||||||
*/
|
*/
|
||||||
public class RDFServiceVirtuoso extends RDFServiceSparql {
|
public class RDFServiceVirtuoso extends RDFServiceSparql {
|
||||||
|
private static final Log log = LogFactory.getLog(RDFServiceVirtuoso.class);
|
||||||
|
|
||||||
public RDFServiceVirtuoso(String readEndpointURI, String updateEndpointURI,
|
private final String username;
|
||||||
String defaultWriteGraphURI) {
|
private final String password;
|
||||||
super(readEndpointURI, updateEndpointURI, defaultWriteGraphURI);
|
|
||||||
|
public RDFServiceVirtuoso(String baseURI, String username, String password) {
|
||||||
|
super(figureReadEndpointUri(baseURI), figureUpdateEndpointUri(baseURI,
|
||||||
|
username));
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RDFServiceVirtuoso(String readEndpointURI, String updateEndpointURI) {
|
private static String figureReadEndpointUri(String baseUri) {
|
||||||
super(readEndpointURI, updateEndpointURI);
|
return noTrailingSlash(baseUri) + "/sparql/";
|
||||||
}
|
}
|
||||||
|
|
||||||
public RDFServiceVirtuoso(String endpointURI) {
|
private static String figureUpdateEndpointUri(String baseUri,
|
||||||
super(endpointURI);
|
String username) {
|
||||||
|
return noTrailingSlash(baseUri) + "/DAV/home/" + username
|
||||||
|
+ "/rdf_sink/vitro_update";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String noTrailingSlash(String uri) {
|
||||||
|
return uri.endsWith("/") ? uri.substring(0, uri.length() - 1) : uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void executeUpdate(String updateString)
|
protected void executeUpdate(String updateString)
|
||||||
throws RDFServiceException {
|
throws RDFServiceException {
|
||||||
super.executeUpdate(updateString.replace("INSERT DATA", "INSERT"));
|
updateString = tweakUpdateStringSyntax(updateString);
|
||||||
|
log.debug("UPDATE STRING: " + updateString);
|
||||||
|
|
||||||
|
try {
|
||||||
|
CloseableHttpResponse response = httpClient.execute(
|
||||||
|
createHttpRequest(updateString), createHttpContext());
|
||||||
|
try {
|
||||||
|
int statusCode = response.getStatusLine().getStatusCode();
|
||||||
|
if (statusCode > 399) {
|
||||||
|
log.error("response " + response.getStatusLine()
|
||||||
|
+ " to update. \n");
|
||||||
|
|
||||||
|
InputStream content = response.getEntity().getContent();
|
||||||
|
for (String line : IOUtils.readLines(content)) {
|
||||||
|
log.error("response-line >>" + line);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RDFServiceException(
|
||||||
|
"Unable to perform SPARQL UPDATE: status code = "
|
||||||
|
+ statusCode);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
response.close();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to update: " + updateString, e);
|
||||||
|
throw new RDFServiceException(
|
||||||
|
"Unable to perform change set update", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String tweakUpdateStringSyntax(String updateString) {
|
||||||
|
if (updateString.startsWith("INSERT DATA")) {
|
||||||
|
return updateString.replaceFirst("INSERT DATA", "INSERT");
|
||||||
|
}
|
||||||
|
return updateString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO entity.setContentType("application/sparql-query");
|
||||||
|
private HttpPost createHttpRequest(String updateString) {
|
||||||
|
HttpPost meth = new HttpPost(updateEndpointURI);
|
||||||
|
meth.addHeader("Content-Type", "application/sparql-query");
|
||||||
|
meth.setEntity(new StringEntity(updateString, "UTF-8"));
|
||||||
|
return meth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Virtuoso has a bug, which it shares with TDB: if given a literal of type
|
* We need an HttpContext that will provide username and password in
|
||||||
|
* response to a basic authentication challenge.
|
||||||
|
*/
|
||||||
|
private HttpClientContext createHttpContext() {
|
||||||
|
CredentialsProvider provider = new BasicCredentialsProvider();
|
||||||
|
provider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(
|
||||||
|
username, password));
|
||||||
|
|
||||||
|
HttpClientContext context = HttpClientContext.create();
|
||||||
|
context.setCredentialsProvider(provider);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtuoso has a bug which it shares with TDB: if given a literal of type
|
||||||
* xsd:nonNegativeInteger, it stores a literal of type xsd:integer.
|
* xsd:nonNegativeInteger, it stores a literal of type xsd:integer.
|
||||||
*
|
*
|
||||||
* To determine whether this serialized graph is equivalent to what is
|
* To determine whether this serialized graph is equivalent to what is
|
||||||
|
|
|
@ -73,22 +73,20 @@ public class ContentTripleSourceSPARQL extends ContentTripleSource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startup(Application application, ComponentStartupStatus ss) {
|
public void startup(Application application, ComponentStartupStatus ss) {
|
||||||
this.rdfServiceFactory = createRDFServiceFactory(createRDFService(ss,
|
this.rdfServiceFactory = createRDFServiceFactory(createRDFService(ss));
|
||||||
endpointURI, updateEndpointURI));
|
|
||||||
this.rdfService = this.rdfServiceFactory.getRDFService();
|
this.rdfService = this.rdfServiceFactory.getRDFService();
|
||||||
this.dataset = createDataset();
|
this.dataset = createDataset();
|
||||||
this.modelMaker = createModelMaker();
|
this.modelMaker = createModelMaker();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected RDFService createRDFService(ComponentStartupStatus ss,
|
protected RDFService createRDFService(ComponentStartupStatus ss) {
|
||||||
String endpoint, String updateEndpoint) {
|
if (updateEndpointURI == null) {
|
||||||
if (updateEndpoint == null) {
|
ss.info("Using endpoint at " + endpointURI);
|
||||||
ss.info("Using endpoint at " + endpoint);
|
return new RDFServiceSparql(endpointURI);
|
||||||
return new RDFServiceSparql(endpoint);
|
|
||||||
} else {
|
} else {
|
||||||
ss.info("Using read endpoint at " + endpoint
|
ss.info("Using read endpoint at " + endpointURI
|
||||||
+ " and update endpoint at " + updateEndpoint);
|
+ " and update endpoint at " + updateEndpointURI);
|
||||||
return new RDFServiceSparql(endpoint, updateEndpoint);
|
return new RDFServiceSparql(endpointURI, updateEndpointURI);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,36 +7,79 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
|
||||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.virtuoso.RDFServiceVirtuoso;
|
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.virtuoso.RDFServiceVirtuoso;
|
||||||
import edu.cornell.mannlib.vitro.webapp.triplesource.impl.sparql.ContentTripleSourceSPARQL;
|
import edu.cornell.mannlib.vitro.webapp.triplesource.impl.sparql.ContentTripleSourceSPARQL;
|
||||||
import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property;
|
import edu.cornell.mannlib.vitro.webapp.utils.configuration.Property;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.utils.configuration.Validation;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* So far, it's just like a ContentTripleSourceSPARQL but it uses an instance of
|
* So far, it's just like a ContentTripleSourceSPARQL but it uses an instance of
|
||||||
* RDFServiceVirtuoso.
|
* RDFServiceVirtuoso.
|
||||||
*/
|
*/
|
||||||
public class ContentTripleSourceVirtuoso extends ContentTripleSourceSPARQL {
|
public class ContentTripleSourceVirtuoso extends ContentTripleSourceSPARQL {
|
||||||
|
private String baseUri;
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
|
||||||
@Override
|
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasBaseURI")
|
||||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasEndpointURI")
|
public void setBaseUri(String uri) {
|
||||||
public void setEndpointURI(String eUri) {
|
if (baseUri == null) {
|
||||||
super.setEndpointURI(eUri);
|
baseUri = uri;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasUpdateEndpointURI")
|
|
||||||
public void setUpdateEndpointURI(String ueUri) {
|
|
||||||
super.setUpdateEndpointURI(ueUri);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected RDFService createRDFService(ComponentStartupStatus ss,
|
|
||||||
String endpoint, String updateEndpoint) {
|
|
||||||
if (updateEndpoint == null) {
|
|
||||||
ss.info("Using endpoint at " + endpoint);
|
|
||||||
return new RDFServiceVirtuoso(endpoint);
|
|
||||||
} else {
|
} else {
|
||||||
ss.info("Using read endpoint at " + endpoint
|
throw new IllegalStateException(
|
||||||
+ " and update endpoint at " + updateEndpoint);
|
"Configuration includes multiple instances of BaseURI: "
|
||||||
return new RDFServiceVirtuoso(endpoint, updateEndpoint);
|
+ baseUri + ", and " + uri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasUsername")
|
||||||
|
public void setUsername(String user) {
|
||||||
|
if (username == null) {
|
||||||
|
username = user;
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Configuration includes multiple instances of Username: "
|
||||||
|
+ username + ", and " + user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasPassword")
|
||||||
|
public void setPassword(String pass) {
|
||||||
|
if (password == null) {
|
||||||
|
password = pass;
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Configuration includes multiple instances of Password: "
|
||||||
|
+ password + ", and " + pass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Validation
|
||||||
|
public void validate() throws Exception {
|
||||||
|
if (baseUri == null) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Configuration did not include a BaseURI.");
|
||||||
|
}
|
||||||
|
if (username == null) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Configuration did not include a Username.");
|
||||||
|
}
|
||||||
|
if (password == null) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Configuration did not include a Password.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RDFService createRDFService(ComponentStartupStatus ss) {
|
||||||
|
ss.info("Using Virtuoso at " + baseUri + ", authenticating as "
|
||||||
|
+ username);
|
||||||
|
return new RDFServiceVirtuoso(baseUri, username, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ContentTripleSourceVirtuoso[" + ToString.hashHex(this)
|
||||||
|
+ ", baseUri=" + baseUri + ", username=" + username + "]";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue