Skip to content

Commit

Permalink
GH-4492 use new txn action endpoints in protocol client, backward com…
Browse files Browse the repository at this point in the history
…patible
  • Loading branch information
abrokenjester committed Apr 25, 2023
1 parent ca28feb commit 0adb45e
Show file tree
Hide file tree
Showing 16 changed files with 144 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
Expand Down Expand Up @@ -121,6 +122,8 @@ public class RDF4JProtocolSession extends SPARQLProtocolSession {

private long pingDelay = PINGDELAY;

private int serverProtocolVersion = 0;

/**
* @deprecated Use {@link #RDF4JProtocolSession(HttpClient, ExecutorService)} instead
*/
Expand Down Expand Up @@ -165,6 +168,7 @@ public void setServerURL(String serverURL) {
}

this.serverURL = serverURL;
this.serverProtocolVersion = 0; // side effect
}

public String getServerURL() {
Expand Down Expand Up @@ -275,6 +279,16 @@ public String getServerProtocol() throws IOException, RepositoryException, Unaut
}
}

private int getServerProtocolVersion() throws UnauthorizedException, RepositoryException, IOException {
if (serverProtocolVersion > 0) {
return serverProtocolVersion;
}

var protocolVersionString = getServerProtocol();
serverProtocolVersion = Integer.parseInt(protocolVersionString);
return serverProtocolVersion;
}

/*-------------------------*
* Repository/context size *
*-------------------------*/
Expand All @@ -286,9 +300,8 @@ public long size(Resource... contexts) throws IOException, RepositoryException,
String transactionURL = getTransactionURL();
final boolean useTransaction = transactionURL != null;

String baseLocation = useTransaction ? appendAction(transactionURL, Action.SIZE)
: Protocol.getSizeLocation(getQueryURL());
URIBuilder url = new URIBuilder(baseLocation);
URIBuilder url = useTransaction ? getTxnActionURIBuilder(Action.SIZE)
: new URIBuilder(Protocol.getSizeLocation(getQueryURL()));

String[] encodedContexts = Protocol.encodeContexts(contexts);
for (int i = 0; i < encodedContexts.length; i++) {
Expand Down Expand Up @@ -591,8 +604,8 @@ public void getStatements(Resource subj, IRI pred, Value obj, boolean includeInf
String transactionURL = getTransactionURL();
final boolean useTransaction = transactionURL != null;

String baseLocation = useTransaction ? transactionURL : Protocol.getStatementsLocation(getQueryURL());
URIBuilder url = new URIBuilder(baseLocation);
URIBuilder url = useTransaction ? getTxnActionURIBuilder(Action.GET)
: new URIBuilder(Protocol.getStatementsLocation(getQueryURL()));

if (subj != null) {
url.setParameter(Protocol.SUBJECT_PARAM_NAME, Protocol.encodeValue(subj));
Expand All @@ -607,9 +620,6 @@ public void getStatements(Resource subj, IRI pred, Value obj, boolean includeInf
url.addParameter(Protocol.CONTEXT_PARAM_NAME, encodedContext);
}
url.setParameter(Protocol.INCLUDE_INFERRED_PARAM_NAME, Boolean.toString(includeInferred));
if (useTransaction) {
url.setParameter(Protocol.ACTION_PARAM_NAME, Action.GET.toString());
}

HttpRequestBase method = useTransaction ? new HttpPut(url.build()) : new HttpGet(url.build());
method = applyAdditionalHeaders(method);
Expand Down Expand Up @@ -683,6 +693,24 @@ public synchronized void beginTransaction(TransactionSetting... transactionSetti
}
}

private URIBuilder getTxnActionURIBuilder(Action action)
throws RDF4JException, IOException, UnauthorizedException {
Objects.requireNonNull(action);
try {
if (getServerProtocolVersion() < 14) { // use legacy action parameter instead of dedicated action endpoint
URIBuilder builder = new URIBuilder(transactionURL);
builder.addParameter(Protocol.ACTION_PARAM_NAME, action.toString());
return builder;
}

URIBuilder builder = new URIBuilder(transactionURL + "/" + action.toString().toLowerCase());
return builder;
} catch (URISyntaxException e) {
logger.error("could not create URL for transaction " + action, e);
throw new RuntimeException(e);
}
}

public synchronized void prepareTransaction() throws RDF4JException, IOException, UnauthorizedException {
checkRepositoryURL();

Expand All @@ -692,9 +720,8 @@ public synchronized void prepareTransaction() throws RDF4JException, IOException

HttpPut method = null;
try {
URIBuilder url = new URIBuilder(transactionURL);
url.addParameter(Protocol.ACTION_PARAM_NAME, Action.PREPARE.toString());
method = applyAdditionalHeaders(new HttpPut(url.build()));
var uriBuilder = getTxnActionURIBuilder(Action.PREPARE);
method = applyAdditionalHeaders(new HttpPut(uriBuilder.build()));

final HttpResponse response = execute(method);
try {
Expand Down Expand Up @@ -725,9 +752,8 @@ public synchronized void commitTransaction() throws RDF4JException, IOException,

HttpPut method = null;
try {
URIBuilder url = new URIBuilder(transactionURL);
url.addParameter(Protocol.ACTION_PARAM_NAME, Action.COMMIT.toString());
method = applyAdditionalHeaders(new HttpPut(url.build()));
var uriBuilder = getTxnActionURIBuilder(Action.COMMIT);
method = applyAdditionalHeaders(new HttpPut(uriBuilder.build()));

final HttpResponse response = execute(method);
try {
Expand Down Expand Up @@ -804,11 +830,10 @@ void executeTransactionPing() {
if (transactionURL == null) {
return; // transaction has already been closed
}
HttpPost method;
try {
URIBuilder url = new URIBuilder(transactionURL);
url.addParameter(Protocol.ACTION_PARAM_NAME, Action.PING.toString());
method = applyAdditionalHeaders(new HttpPost(url.build()));
var uriBuilder = getTxnActionURIBuilder(Action.PING);
var method = applyAdditionalHeaders(new HttpPost(uriBuilder.build()));

String text = EntityUtils.toString(executeOK(method).getEntity());
long timeout = Long.parseLong(text);
// clients should ping before server timeouts transaction
Expand All @@ -824,16 +849,16 @@ void executeTransactionPing() {
pingTransaction(); // reschedule
}

/**
* Appends the action as a parameter to the supplied url
*
* @param url a url on which to append the parameter. it is assumed the url has no parameters.
* @param action the action to add as a parameter
* @return the url parametrized with the supplied action
*/
private String appendAction(String url, Action action) {
return url + "?" + Protocol.ACTION_PARAM_NAME + "=" + action.toString();
}
// /**
// * Appends the action as a parameter to the supplied url
// *
// * @param url a url on which to append the parameter. it is assumed the url has no parameters.
// * @param action the action to add as a parameter
// * @return the url parametrized with the supplied action
// */
// private String appendAction(String url, Action action) {
// return url + "?" + Protocol.ACTION_PARAM_NAME + "=" + action.toString();
// }

/**
* Sends a transaction list as serialized XML to the server.
Expand Down Expand Up @@ -938,9 +963,16 @@ protected HttpUriRequest getQueryMethod(QueryLanguage ql, String query, String b
RequestBuilder builder;
String transactionURL = getTransactionURL();
if (transactionURL != null) {
builder = RequestBuilder.put(transactionURL);
URI requestURI;
try {
requestURI = getTxnActionURIBuilder(Action.QUERY).build();
} catch (URISyntaxException | IOException e) {
logger.error("could not create URL for transaction query", e);
throw new RuntimeException(e);
}

builder = RequestBuilder.put(requestURI);
builder.setHeader("Content-Type", Protocol.SPARQL_QUERY_MIME_TYPE + "; charset=utf-8");
builder.addParameter(Protocol.ACTION_PARAM_NAME, Action.QUERY.toString());
for (NameValuePair nvp : getQueryMethodParameters(ql, null, baseURI, dataset, includeInferred, maxQueryTime,
bindings)) {
builder.addParameter(nvp);
Expand Down Expand Up @@ -971,9 +1003,17 @@ protected HttpUriRequest getUpdateMethod(QueryLanguage ql, String update, String
RequestBuilder builder;
String transactionURL = getTransactionURL();
if (transactionURL != null) {
builder = RequestBuilder.put(transactionURL);

URI requestURI;
try {
requestURI = getTxnActionURIBuilder(Action.UPDATE).build();
} catch (URISyntaxException | IOException e) {
logger.error("could not create URL for transaction update", e);
throw new RuntimeException(e);
}

builder = RequestBuilder.put(requestURI);
builder.addHeader("Content-Type", Protocol.SPARQL_UPDATE_MIME_TYPE + "; charset=utf-8");
builder.addParameter(Protocol.ACTION_PARAM_NAME, Action.UPDATE.toString());
for (NameValuePair nvp : getUpdateMethodParameters(ql, null, baseURI, dataset, includeInferred,
maxExecutionTime, bindings)) {
builder.addParameter(nvp);
Expand Down Expand Up @@ -1061,36 +1101,28 @@ protected void upload(HttpEntity reqEntity, String baseURI, boolean overwrite, b
boolean useTransaction = transactionURL != null;

try {

String baseLocation = useTransaction ? transactionURL : Protocol.getStatementsLocation(getQueryURL());
URIBuilder url = new URIBuilder(baseLocation);
URIBuilder uriBuilder = useTransaction ? getTxnActionURIBuilder(action)
: new URIBuilder(Protocol.getStatementsLocation(getQueryURL()));

// Set relevant query parameters
for (String encodedContext : Protocol.encodeContexts(contexts)) {
url.addParameter(Protocol.CONTEXT_PARAM_NAME, encodedContext);
uriBuilder.addParameter(Protocol.CONTEXT_PARAM_NAME, encodedContext);
}
if (baseURI != null && baseURI.trim().length() != 0) {
String encodedBaseURI = Protocol.encodeValue(SimpleValueFactory.getInstance().createIRI(baseURI));
url.setParameter(Protocol.BASEURI_PARAM_NAME, encodedBaseURI);
uriBuilder.setParameter(Protocol.BASEURI_PARAM_NAME, encodedBaseURI);
}
if (preserveNodeIds) {
url.setParameter(Protocol.PRESERVE_BNODE_ID_PARAM_NAME, "true");
}

if (useTransaction) {
if (action == null) {
throw new IllegalArgumentException("action can not be null on transaction operation");
}
url.setParameter(Protocol.ACTION_PARAM_NAME, action.toString());
uriBuilder.setParameter(Protocol.PRESERVE_BNODE_ID_PARAM_NAME, "true");
}

// Select appropriate HTTP method
HttpEntityEnclosingRequestBase method = null;
try {
if (overwrite || useTransaction) {
method = applyAdditionalHeaders(new HttpPut(url.build()));
method = applyAdditionalHeaders(new HttpPut(uriBuilder.build()));
} else {
method = applyAdditionalHeaders(new HttpPost(url.build()));
method = applyAdditionalHeaders(new HttpPost(uriBuilder.build()));
}

// Set payload
Expand Down Expand Up @@ -1217,4 +1249,9 @@ private <T extends HttpUriRequest> T applyAdditionalHeaders(T method) {
}
return method;
}

private boolean useDeprecatedTxnActions()
throws UnauthorizedException, NumberFormatException, RepositoryException, IOException {
return Integer.parseInt(getServerProtocol()) < 14;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,15 @@ public enum TIMEOUT {
* Protocol version.
*
* <ul>
* <li>14: since RDF4J 4.3.0</li>
* <li>13: since RDF4J 4.0.0</li>
* <li>12: since RDF4J 3.5.0</li>
* <li>11: since RDF4J 3.3.0</li>
* <li>10: since RDF4J 3.1.0</li>
* <li>9: since RDF4J 3.0.0</li>
* </ul>
*/
public static final String VERSION = "12";
public static final String VERSION = "14";

/**
* Parameter name for the 'subject' parameter of a statement query.
Expand Down
37 changes: 37 additions & 0 deletions site/content/documentation/reference/rest-api/changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
title: "RDF4J REST API: Changelog"
toc: true
weight: 1
---

[OpenAPI specification](/documentation/reference/rest-api/)

## Version history

The RDF4J REST API uses a single integer version numbering scheme. It does not follow semantic versioning. However, the implementations of the API client and server in RDF4J make a conscious effort to stay backward compatible with at least one previous protocol version.

### 14: since RDF4J 4.3.0

- Replaced usage of the `action` parameter on the `/transactions/{txnID}` endpoint with separate endpoints for each available action: `/transactions/{txnID}/add`, `/transactions/{txnID}/query`, and so on.
- `action` parameter for transactions is deprecated

### 13: since RDF4J 4.0.0

- Removed support for the deprecated SYSTEM repository.

### 12: since RDF4J 3.5.0

- Added suport for the `prepare` transaction operation.

### 11: since RDF4J 3.3.0

- Added support for sending general transaction setting parameters.

### 10: since RDF4J 3.1.0

- Added support for retrieving a repository configuration.

### 9: since RDF4J 3.0.0

- Added direct API support for creating a new repository remotely and/or updating an existing repository's configuration.
- SYSTEM repository support is deprecated.
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
openapi: "3.0.1"
info:
title: RDF4J REST API
version: "10"
version: "14"
description: |
The RDF4J REST API is an HTTP protocol that covers a fully compliant implementation of the [SPARQL 1.1 Protocol W3C Recommendation](https://www.w3.org/TR/sparql11-protocol/). This ensures that RDF4J Server functions as a fully standard-compliant [SPARQL](https://www.w3.org/TR/sparql11-query/) endpoint.
The RDF4J REST API additionally supports the [SPARQL 1.1 Graph Store HTTP Protocol W3C Recommendation](https://www.w3.org/TR/sparql11-http-rdf-update/). The RDF4J REST API extends the W3C standards in several aspects, the most important of which is database transaction management.
Version 13 was released as part of RDF4J 4.3.0. See the [REST API Changelog](/documentation/reference/rest-api/changelog) for details.
externalDocs:
url: https://rdf4j.org/documentation/reference/rest-api/
url: https://rdf4j.org/documentation/reference/rest-api/changelog.md

servers:
- url: http://localhost:8080/rdf4j-server/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
package org.eclipse.rdf4j.http.server.repository.transaction;

import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;

import static org.eclipse.rdf4j.http.protocol.Protocol.CONTEXT_PARAM_NAME;

import java.nio.charset.Charset;
Expand Down Expand Up @@ -91,17 +92,17 @@ protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpSer
public void destroy() throws Exception {
ActiveTransactionRegistry.INSTANCE.destroyScheduler();
}

/**
* Handle the specific action as part of the supplied {@link Transaction} object.
*
*
* @param request the request
* @param transaction the transaction on which the action is to be executed
* @return result of the action (may not be null)
*/
protected abstract ModelAndView handleAction(HttpServletRequest request, HttpServletResponse response,
Transaction transaction) throws Exception;


static RDFFormat getRDFFormat(HttpServletRequest request) {
return Rio.getParserFormatForMIMEType(request.getContentType())
.orElseThrow(Rio.unsupportedFormat(request.getContentType()));
Expand Down Expand Up @@ -155,5 +156,4 @@ private UUID getTransactionID(HttpServletRequest request) throws ClientHTTPExcep
return txnID;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php
* http://www.eclipse.org/org/documents/edl-v10.php
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php
* http://www.eclipse.org/org/documents/edl-v10.php
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Distribution License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/org/documents/edl-v10.php
* http://www.eclipse.org/org/documents/edl-v10.php
*
* SPDX-License-Identifier: BSD-3-Clause
*******************************************************************************/
Expand Down
Loading

0 comments on commit 0adb45e

Please sign in to comment.