All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.fbk.knowledgestore.Outcome Maven / Gradle / Ivy

Go to download

The Core module (ks-core) contains core abstractions and basic functionalities shared by the KnowledgeStore Frontend Server and the Java Client. It also defines the Java version of the KnowledgeStore API.

The newest version!
package eu.fbk.knowledgestore;

import java.io.Serializable;
import java.util.Map;

import javax.annotation.Nullable;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;

import org.openrdf.model.Statement;
import org.openrdf.model.URI;

import eu.fbk.knowledgestore.data.Data;
import eu.fbk.knowledgestore.data.Record;
import eu.fbk.knowledgestore.data.Stream;
import eu.fbk.knowledgestore.vocabulary.KSR;

/**
 * The outcome of the invocation of a KnowledgeStore operation.
 * 

* An {@code Outcome} instance encodes the outcome of an {@code Operation} invocation. It stores: *

*
    *
  • the {@link Status} code specifying whether and how the invocation was successful or failed * (see {@link #getStatus()});
  • *
  • the invocation ID, which is generated by the system and may link to additional information * logged on client/server sides (see {@link #getInvocationID()});
  • *
  • an optional object ID, in case the invocation was applied to a specific object (see * {@link #getObjectID()});
  • *
  • an optional message, providing additional information about the outcome (see * {@link #getMessage()})
  • *
*

* {@code Outcome} instances can be created using one of the static factory methods * {@link #create(Status, URI)}, {@link #create(Status, URI, URI)}, * {@link #create(Status, URI, URI, String)}. Equality and comparison are performed based on the * invocation ID. This class is immutable and thus thread safe. *

*/ public final class Outcome implements Comparable, Serializable { private static final long serialVersionUID = 1L; private final Status status; private final URI invocationID; @Nullable private final URI objectID; @Nullable private final String message; private Outcome(final Status status, final URI invocationID, @Nullable final URI objectID, @Nullable final String message) { this.status = Preconditions.checkNotNull(status); this.invocationID = Preconditions.checkNotNull(invocationID); this.objectID = objectID; this.message = message; } /** * Creates a new {@code Outcome} with the status code and invocation ID supplied. * * @param status * the status code, not null * @param invocationID * the invocation ID, not null * @return the created {@code Outcome} */ public static Outcome create(final Status status, final URI invocationID) { return create(status, invocationID, null, null); } /** * Creates a new {@code Outcome} with the status code, invocation ID and optional object ID * supplied. * * @param status * the status code, not null * @param invocationID * the invocation ID, not null * @param objectID * the optional object ID, possibly null * @return the created {@code Outcome} */ public static Outcome create(final Status status, final URI invocationID, @Nullable final URI objectID) { return create(status, invocationID, objectID, null); } /** * Creates a new {@code Outcome} with the status code, invocation ID, optional object ID and * optional message supplied. * * @param status * the status code, not null * @param invocationID * the invocation ID, not null * @param objectID * the optional object ID, possibly null * @param message * the optional message, possibly null * @return the created {@code Outcome} */ public static Outcome create(final Status status, final URI invocationID, @Nullable final URI objectID, @Nullable final String message) { return new Outcome(status, invocationID, objectID, message); } /** * Creates a new {@code Outcome} starting from the {@code Record} supplied. The record ID is * used as the invocation ID, while properties {@link KSR#STATUS}, {@link KSR#OBJECT} and * {@link KSR#MESSAGE} are read, respectively, to recover the status code, the optional object * ID and the optional message. * * @param record * the record * @return the created {@code Outcome} */ public static Outcome create(final Record record) { final URI invocationID = record.getID(); final Status status = Status.valueOf(record.getUnique(KSR.STATUS, URI.class)); final URI objectID = record.getUnique(KSR.OBJECT, URI.class); final String message = record.getUnique(KSR.MESSAGE, String.class); return create(status, invocationID, objectID, message); } /** * Returns the status code for this outcome. This property can be checked to determine whether * the invocation was successful or resulted in an error, with different status codes * specifying different success or error conditions. * * @return the status code, not null */ public Status getStatus() { return this.status; } /** * Returns the ID of the invocation, which may link to relevant logged information. * * @return the invocation ID, not null */ public URI getInvocationID() { return this.invocationID; } /** * Returns the ID of the processed object, if applicable. * * @return the object ID, null if not applicable */ @Nullable public URI getObjectID() { return this.objectID; } /** * Return an optional message providing further information about the outcome. * * @return the optional message */ @Nullable public String getMessage() { return this.message; } /** * {@inheritDoc} Comparison is done on the invocation ID. */ @Override public int compareTo(final Outcome other) { return Data.getTotalComparator().compare(this.invocationID, other.invocationID); } /** * {@inheritDoc} Two {@code Outcome} instances are equal if the have the same invocation ID. */ @Override public boolean equals(@Nullable final Object object) { if (object == this) { return true; } if (!(object instanceof Outcome)) { return false; } final Outcome other = (Outcome) object; return this.invocationID.equals(other.invocationID); } /** * {@inheritDoc} The returned code depends on the invocation ID. */ @Override public int hashCode() { return this.invocationID.hashCode(); } /** * Return a {@code Record} version of this {@code Outcome} object. The returned record is * identified by this invocation ID; it is associated to the status URI via property * {@link KSR#STATUS}, to the optional object ID via property {@link KSR#OBJECT} and to the * optional message via property {@link KSR#MESSAGE}. * * @return the corresponding record */ public Record toRecord() { final Record record = Record.create(this.invocationID); record.add(KSR.STATUS, this.status.getURI()); if (this.objectID != null) { record.add(KSR.OBJECT, this.objectID); } if (this.message != null) { record.add(KSR.MESSAGE, Data.getValueFactory().createLiteral(this.message)); } return record; } /** * {@inheritDoc} The method returns a string of the form * {@code status invocationID (objectID) message}. */ @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append(this.status); builder.append(' '); builder.append(Data.toString(this.invocationID, Data.getNamespaceMap())); if (this.objectID != null) { builder.append(' '); builder.append('('); builder.append(Data.toString(this.objectID, Data.getNamespaceMap())); builder.append(')'); } if (this.message != null) { builder.append(' '); builder.append(this.message); } return builder.toString(); } /** * Performs outcome-to-RDF encoding by converting a stream of outcomes in a stream of RDF * statements. * * @param stream * the stream of outcomes to encode. * @return the resulting stream of statements */ public static Stream encode(final Stream stream) { Preconditions.checkNotNull(stream); return Record.encode(stream.transform(new Function() { @Override public Record apply(final Outcome outcome) { return outcome.toRecord(); } }, 0), ImmutableSet.of(KSR.INVOCATION)); } /** * Performs RDF-to-outcome decoding by converting a stream of RDF statements in a stream of * outcomes. Parameter {@code chunked} specifies whether the input statement stream is * chunked, i.e., organized as a sequence of statement chunks with each chunk containing the * statements for an outcome. Chunked RDF streams noticeably speed up decoding, and are always * produced by the KnowledgeStore API. Chunking information may be set to null (e.g., because * unknown at the time the method is called): in this case, it will be read from metadata * attribute {@code "chunked"} attached to the stream; reading will happen just before * decoding will take place, i.e., when a terminal stream operation will be called. * * @param stream * the stream of statements to decode * @param chunked * true if the input statement stream is chunked, null if to be read from stream * metadata * @return the resulting stream of outcomes */ public static Stream decode(final Stream stream, @Nullable final Boolean chunked) { Preconditions.checkNotNull(stream); return Record.decode(stream, ImmutableSet.of(KSR.INVOCATION), chunked).transform( new Function() { @Override public Outcome apply(final Record record) { return Outcome.create(record); } }, 0); } /** * Enumeration of {@code Outcome} status codes. *

* This enumeration lists the possible status codes for {@code Outcome} objects. A status code * is identified by an URI (see {@link #getURI()}), is briefly explained through a comment * string (see {@link #getComment()}) and may be mapped to an HTTP status code (see * {@link #getHTTPStatus()}). Methods {@link #isOK()}, {@link #isError()} determine, * respectively, if the status code denotes a success or error situation. Lookup of status * codes based on URI is supported by method {@link #valueOf(URI)}. *

*/ public enum Status { /** * Success status specifying successful completion of a bulk operation invocation for all * the objects involved. * * @see KSR#OK_BULK */ OK_BULK(KSR.OK_BULK, "Bulk operation succeeded for all affected objects", 200, true), /** * Success status specifying that an object has been created. * * @see KSR#OK_CREATED */ OK_CREATED(KSR.OK_CREATED, "Object created", 201, true), /** * Success status specifying that an object has been modified. * * @see KSR#OK_MODIFIED */ OK_MODIFIED(KSR.OK_MODIFIED, "Object modified", 200, true), /** * Success status specifying that it was not necessary to modify an object. * * @see KSR#OK_UNMODIFIED */ OK_UNMODIFIED(KSR.OK_UNMODIFIED, "Object not modified", 200, true), /** * Success status specifying that an object has been deleted. * * @see KSR#OK_DELETED */ OK_DELETED(KSR.OK_DELETED, "Object deleted", 200, true), /** * Error status specifying that a bulk operation invocation failed for one or more of the * involved objects. * * @see KSR#ERROR_BULK */ ERROR_BULK(KSR.ERROR_BULK, "Bulk operation failed for at least one affected object", 200, false), /** * Error status specifying that an operation invocation failed as its result would not be * acceptable to the client. * * @see KSR#ERROR_PRECONDITION_FAILED */ ERROR_NOT_ACCEPTABLE(KSR.ERROR_NOT_ACCEPTABLE, "Operation failed as result would not be acceptable to client", 406, false), /** * Error status specifying that the referenced object does not exist. * * @see KSR#ERROR_OBJECT_NOT_FOUND */ ERROR_OBJECT_NOT_FOUND(KSR.ERROR_OBJECT_NOT_FOUND, "Operation failed as target object does not exist", 404, false), /** * Error status specifying that the referenced object already exists. * * @see KSR#ERROR_OBJECT_ALREADY_EXISTS */ ERROR_OBJECT_ALREADY_EXISTS(KSR.ERROR_OBJECT_ALREADY_EXISTS, "Operation failed as target object already exists", 409, false), /** * Error status specifying that an object indirectly referenced by the operation * invocation does not exist. * * @see KSR#ERROR_DEPENDENCY_NOT_FOUND */ ERROR_DEPENDENCY_NOT_FOUND(KSR.ERROR_DEPENDENCY_NOT_FOUND, "Operation failed as object it depends on does not exist", 400, false), /** * Error status specifying that input arguments are missing or wrong. * * @see KSR#ERROR_INVALID_INPUT */ ERROR_INVALID_INPUT(KSR.ERROR_INVALID_INPUT, "Operation failed as required input arguments are missing or wrong", 400, false), /** * Error status specifying that the invoked operation has not been executed because the * requester has not enough privileges. * * @see KSR#ERROR_FORBIDDEN */ ERROR_FORBIDDEN(KSR.ERROR_FORBIDDEN, "Operation forbidden", 403, false), /** * Error status specifying that the invoked operation was forcedly interrupted by the * client, server or due to a connectivity problem. * * @see KSR#ERROR_INTERRUPTED */ ERROR_INTERRUPTED(KSR.ERROR_INTERRUPTED, "Operation interrupted", 503, false), /** * Error status specifying that the invocation failed due to an unexpected error. * * @see KSR#ERROR_UNEXPECTED */ ERROR_UNEXPECTED(KSR.ERROR_UNEXPECTED, "Unexpected error", 500, false), /** * Error status specifying that the outcome of the invoked operation is unknown. * * @see KSR#UNKNOWN */ UNKNOWN(KSR.UNKNOWN, "Unknown outcome", 503, false); @Nullable private static Map uriToStatusMap = null; private URI uri; private String comment; private int httpStatus; private boolean isOK; private boolean isError; private Status(final URI uri, final String comment, final int httpStatus, @Nullable final boolean okOrError) { this.uri = uri; this.comment = comment; this.httpStatus = httpStatus; this.isOK = okOrError == Boolean.TRUE; this.isError = okOrError == Boolean.FALSE; } /** * Returns the URI univocally identifying this status code. The URI can be used for * serializing / deserializing this {@code Status} object. * * @return the URI for this status code */ public URI getURI() { return this.uri; } /** * Returns a constant comment string describing this status code. * * @return a comment string */ public String getComment() { return this.comment; } /** * Returns the HTTP status code corresponding to this {@code Outcome} status code. Note * that multiple {@code Outcome} status codes may map to the same HTTP status code. * * @return the corresponding HTTP status code */ public int getHTTPStatus() { return this.httpStatus; } /** * Helper method to determine whether the status code denotes success. * * @return true, if this status code denotes success */ public boolean isOK() { return this.isOK; } /** * Helper method to determine whether the status code denotes error. * * @return true, if this status code denotes error */ public boolean isError() { return this.isError; } /** * Lookups the status code with the URI specified. * * @param uri * the URI of the status code to lookup * @return the corresponding status code, on success * @throws IllegalArgumentException * if there is no status for the URI specified */ public static Status valueOf(final URI uri) throws IllegalArgumentException { if (uriToStatusMap == null) { final ImmutableMap.Builder builder = ImmutableMap.builder(); for (final Status status : Status.values()) { builder.put(status.uri, status); } uriToStatusMap = builder.build(); } final Status status = uriToStatusMap.get(uri); if (status == null) { throw new IllegalArgumentException("Invalid status URI: " + uri); } return status; } /** * Return the {@code Status} that more closely matches the HTTP status code specified. * Note that only part of the statuses of this enuemration are returned, as the mapping * from {@code Status} to HTTP statuses is N:1. * * @param httpStatus * the HTTP status * @return the corresponding {@code Status}, defaulting to {@link #ERROR_UNEXPECTED} */ public static Status valueOf(final int httpStatus) { if (httpStatus == 400) { return Status.ERROR_INVALID_INPUT; } else if (httpStatus == 401 || httpStatus == 403) { return Status.ERROR_FORBIDDEN; } else if (httpStatus == 404) { return Status.ERROR_OBJECT_NOT_FOUND; } else if (httpStatus == 406) { return Status.ERROR_NOT_ACCEPTABLE; } return Outcome.Status.ERROR_UNEXPECTED; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy