
eu.fbk.knowledgestore.server.http.jaxrs.Crud Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ks-server-http Show documentation
Show all versions of ks-server-http Show documentation
The HTTP server module (ks-server-http) implements the Web API of
the KnowledgeStore, which includes the two CRUD and SPARQL endpoints.
The CRUD Endpoint supports the retrieval and manipulation of
semi-structured data about resource, mention, entity and axiom records
(encoded in RDF, possibly using JSONLD), and the upload / download of
resource representation. The SPARQL Endpoint supports SPARQL SELECT,
CONSTRUCT, DESCRIBE and ASK queries according to the W3C SPARQL
protocol. The two endpoints are implemented on top of a component
implementing the KnowledgeStore Java API (the Store interface), which
can be either the the KnowledgeStore frontend (ks-frontend) or the Java
Client. The implementation of the module is based on the Jetty Web sever
(run in embedded mode) and the Jersey JAX-RS implementation. Reference
documentation of the Web API is automatically generated using the
Enunciate tool.
package eu.fbk.knowledgestore.server.http.jaxrs;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.codehaus.enunciate.jaxrs.ResponseCode;
import org.codehaus.enunciate.jaxrs.StatusCodes;
import org.codehaus.enunciate.jaxrs.TypeHint;
import org.openrdf.model.Literal;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.ValueFactory;
import eu.fbk.knowledgestore.Operation;
import eu.fbk.knowledgestore.OperationException;
import eu.fbk.knowledgestore.Outcome;
import eu.fbk.knowledgestore.data.Criteria;
import eu.fbk.knowledgestore.data.Data;
import eu.fbk.knowledgestore.data.Record;
import eu.fbk.knowledgestore.data.Stream;
import eu.fbk.knowledgestore.data.XPath;
import eu.fbk.knowledgestore.internal.jaxrs.Protocol;
import eu.fbk.knowledgestore.vocabulary.KSR;
public abstract class Crud extends Resource {
private static final long DEFAULT_RETRIEVE_LIMIT = 1000;
private final URI recordType;
Crud(final URI recordType) {
this.recordType = Preconditions.checkNotNull(recordType);
}
final URI getRecordType() {
return this.recordType;
}
@GET
@Produces(Protocol.MIME_TYPES_RDF)
@TypeHint(Stream.class)
@StatusCodes({ @ResponseCode(code = 200, condition = "if the request is acceptable and the "
+ "query is being executed") })
public Response retrieve(
@QueryParam(Protocol.PARAMETER_CONDITION) final List conditions,
@QueryParam(Protocol.PARAMETER_ID) final List ids,
@QueryParam(Protocol.PARAMETER_PROPERTY) final List properties,
@QueryParam(Protocol.PARAMETER_OFFSET) final Long offset,
@QueryParam(Protocol.PARAMETER_LIMIT) final Long limit) throws OperationException {
// Apply default limit, if not explicitly given
final long actualLimit = MoreObjects.firstNonNull(limit, DEFAULT_RETRIEVE_LIMIT);
// Prepare the retrieve operation, returning an error if parameters are wrong
final Operation.Retrieve operation;
try {
operation = getSession().retrieve(getRecordType()) //
.timeout(getTimeout()) //
.conditions(emptyToNull(conditions)) //
.ids(emptyToNull(parseURIs(ids))) //
.properties(emptyToNull(parseURIs(properties))) //
.offset(offset) //
.limit(actualLimit);
} catch (final IllegalArgumentException ex) {
throw new OperationException(newOutcome(Outcome.Status.ERROR_INVALID_INPUT,
ex.getMessage()), ex);
}
// Validate client preconditions, using default last modified and tag
init(false, null, null, null);
// Setup the resulting stream (materialized only for GET requests)
Stream entity;
if (getMethod().equals(HttpMethod.HEAD)) {
entity = Stream.create();
} else {
entity = operation.exec();
}
entity.setProperty("types", ImmutableSet.of(getRecordType()));
// Stream the results in the HTTP response
return newResponseBuilder(Status.OK, closeOnCompletion(entity), Protocol.STREAM_OF_RECORDS)
.build();
}
@GET
@Path(Protocol.SUBPATH_COUNT)
@Produces(Protocol.MIME_TYPES_RDF)
@TypeHint(Stream.class)
@StatusCodes({ @ResponseCode(code = 200, condition = "if the request is acceptable and the "
+ "requested count is being returned") })
public Response count( //
@QueryParam(Protocol.PARAMETER_CONDITION) final List conditions, //
@QueryParam(Protocol.PARAMETER_ID) final List ids) throws OperationException {
// Prepare the count operation, returning an error if parameters are wrong
final Operation.Count operation;
try {
operation = getSession() //
.count(getRecordType()) //
.timeout(getTimeout()) //
.conditions(emptyToNull(conditions)) //
.ids(emptyToNull(parseURIs(ids)));
} catch (final IllegalArgumentException ex) {
throw new OperationException(newOutcome(Outcome.Status.ERROR_INVALID_INPUT,
ex.getMessage()), ex);
}
// Validate client preconditions, using default last modified and tag
init(false, null, null, null);
// Setup the resulting stream (materialized only for GET requests)
Stream entity;
if (getMethod().equals(HttpMethod.HEAD)) {
entity = Stream.create();
} else {
final long count = operation.exec();
final ValueFactory factory = Data.getValueFactory();
final Literal literal = factory.createLiteral(count);
final Statement statement = factory.createStatement(getInvocationID(), KSR.RESULT,
literal);
entity = Stream.create(statement);
}
// Return the result in the HTTP response
return newResponseBuilder(Status.OK, closeOnCompletion(entity),
Protocol.STREAM_OF_STATEMENTS).build();
}
@POST
@Path(Protocol.SUBPATH_CREATE)
@Consumes(Protocol.MIME_TYPES_RDF)
@Produces(Protocol.MIME_TYPES_RDF)
@TypeHint(Stream.class)
public Response create(final Stream records) throws OperationException {
// Schedule closing of input entity
closeOnCompletion(records);
// Setup the Create operation, returning an error if parameters are wrong
final Operation.Create operation;
try {
operation = getSession() //
.create(getRecordType()) //
.timeout(getTimeout()) //
.records(records);
} catch (final IllegalArgumentException ex) {
throw new OperationException(newOutcome(Outcome.Status.ERROR_INVALID_INPUT,
ex.getMessage()), ex);
}
// Validate client preconditions and handle probing
init(true, null);
// Setup record decoding
records.setProperty("types", ImmutableList.of(getRecordType()));
closeOnCompletion(records);
// Perform the operation
final List outcomes = Lists.newArrayList();
operation.exec(outcomes);
// Setup the resulting stream
final Stream entity = Stream.create(outcomes);
entity.setProperty("types", ImmutableSet.of(KSR.INVOCATION));
// final Stream entity = new Stream() {
//
// @Override
// protected void doToHandler(final Handler super Outcome> handler) {
// try {
// operation.exec(handler);
// } catch (final Throwable ex) {
// propagateIfNotBulk(ex);
// }
// }
//
// };
// Stream the results in the HTTP response
return newResponseBuilder(Status.OK, entity, Protocol.STREAM_OF_OUTCOMES).build();
}
@POST
@Path(Protocol.SUBPATH_MERGE)
@Consumes(Protocol.MIME_TYPES_RDF)
@Produces(Protocol.MIME_TYPES_RDF)
@TypeHint(Stream.class)
public Response merge( //
@QueryParam(Protocol.PARAMETER_CRITERIA) final List criterias, //
final Stream records) throws OperationException {
// Schedule closing of input entity
closeOnCompletion(records);
// Setup the merge operation, returning an error if parameters are wrong
final Operation.Merge operation;
try {
operation = getSession() //
.merge(getRecordType()) //
.timeout(getTimeout()) //
.records(records) //
.criteria(emptyToNull(criterias));
} catch (final IllegalArgumentException ex) {
throw new OperationException(newOutcome(Outcome.Status.ERROR_INVALID_INPUT,
ex.getMessage()), ex);
}
// Validate client preconditions
init(true, null);
// Setup record decoding
records.setProperty("types", ImmutableList.of(getRecordType()));
closeOnCompletion(records);
// Perform the operation
final List outcomes = Lists.newArrayList();
operation.exec(outcomes);
// Setup the resulting stream
final Stream entity = Stream.create(outcomes);
entity.setProperty("types", ImmutableSet.of(KSR.INVOCATION));
// final Stream entity = new Stream() {
//
// @Override
// protected void doToHandler(final Handler super Outcome> handler) {
// try {
// operation.exec(handler);
// } catch (final Throwable ex) {
// propagateIfNotBulk(ex);
// }
// }
//
// };
// Stream the results in the HTTP response
return newResponseBuilder(Status.OK, entity, Protocol.STREAM_OF_OUTCOMES).build();
}
@POST
@Path(Protocol.SUBPATH_UPDATE)
@Consumes(Protocol.MIME_TYPES_RDF)
@Produces(Protocol.MIME_TYPES_RDF)
@TypeHint(Stream.class)
public Response update( //
@QueryParam(Protocol.PARAMETER_CONDITION) final List conditions, //
@QueryParam(Protocol.PARAMETER_ID) final List ids, //
@QueryParam(Protocol.PARAMETER_CRITERIA) final List criterias, //
final Stream records) throws OperationException {
// Schedule closing of input entity
closeOnCompletion(records);
// Setup the update operation (apart record), returning an error if parameters are wrong
final Operation.Update operation;
try {
operation = getSession() //
.update(getRecordType()) //
.timeout(getTimeout()) //
.conditions(emptyToNull(conditions)) //
.ids(emptyToNull(ids)) //
.criteria(emptyToNull(criterias));
} catch (final IllegalArgumentException ex) {
throw new OperationException(newOutcome(Outcome.Status.ERROR_INVALID_INPUT,
ex.getMessage()), ex);
}
// Validate client preconditions and handle probing
init(true, null);
// Decode input record
records.setProperty("types", ImmutableList.of(getRecordType()));
final Record record = records.getUnique();
// Perform the operation
final List outcomes = Lists.newArrayList();
operation.record(record).exec(outcomes);
// Setup the resulting stream
final Stream entity = Stream.create(outcomes);
entity.setProperty("types", ImmutableSet.of(KSR.INVOCATION));
// final Stream entity = new Stream() {
//
// @Override
// protected void doToHandler(final Handler super Outcome> handler) {
// try {
// operation.record(record).exec(handler);
// } catch (final Throwable ex) {
// propagateIfNotBulk(ex);
// }
// }
//
// };
// Stream the results in the HTTP response
return newResponseBuilder(Status.OK, entity, Protocol.STREAM_OF_OUTCOMES).build();
}
@POST
@Path(Protocol.SUBPATH_DELETE)
@Produces(Protocol.MIME_TYPES_RDF)
@TypeHint(Stream.class)
public Response delete( //
@QueryParam(Protocol.PARAMETER_CONDITION) final List conditions, //
@QueryParam(Protocol.PARAMETER_ID) final List ids) throws OperationException {
// Setup the delete operation, returning an error if parameters are wrong
final Operation.Delete operation;
try {
operation = getSession() //
.delete(getRecordType()) //
.timeout(getTimeout()) //
.conditions(emptyToNull(conditions)) //
.ids(emptyToNull(ids));
} catch (final IllegalArgumentException ex) {
throw new OperationException(newOutcome(Outcome.Status.ERROR_INVALID_INPUT,
ex.getMessage()), ex);
}
// Validate client preconditions and handle probing
init(true, null);
// Perform the operation
final List outcomes = Lists.newArrayList();
operation.exec(outcomes);
// Setup the resulting stream
final Stream entity = Stream.create(outcomes);
entity.setProperty("types", ImmutableSet.of(KSR.INVOCATION));
// final Stream entity = new Stream() {
//
// @Override
// protected void doToHandler(final Handler super Outcome> handler) {
// try {
// operation.exec(handler);
// } catch (final Throwable ex) {
// propagateIfNotBulk(ex);
// }
// }
//
// };
// Stream the results in the HTTP response
return newResponseBuilder(Status.OK, closeOnCompletion(entity),
Protocol.STREAM_OF_OUTCOMES).build();
}
private > T emptyToNull(final T iterable) {
return iterable == null || Iterables.isEmpty(iterable) ? null : iterable;
}
private List parseURIs(final Iterable strings) {
final List uris = Lists.newArrayList();
for (final String string : strings) {
final int length = string.length();
boolean escape = false;
boolean qname = false;
int start = -1;
for (int i = 0; i < length; ++i) {
final char ch = string.charAt(i);
if (escape) {
escape = false;
} else if (start >= 0) {
if (qname) {
if (ch == ',' || ch == ';' || Character.isWhitespace(ch)) {
uris.add((URI) Data.parseValue(string.substring(start, i),
Data.getNamespaceMap()));
start = -1;
}
} else {
if (ch == '\\') {
escape = true;
} else if (ch == '>') {
uris.add((URI) Data.parseValue(string.substring(start, i + 1),
Data.getNamespaceMap()));
start = -1;
}
}
} else if (ch == '<') {
start = i;
qname = false;
} else if (!Character.isWhitespace(ch)) {
start = i;
qname = true;
}
}
if (start >= 0) {
if (qname) {
uris.add((URI) Data.parseValue(string.substring(start), Data.getNamespaceMap()));
} else {
throw new IllegalArgumentException("Invalid ID(s): " + string);
}
}
}
return uris;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy