First steps in reducing dependency on proto from runtime.
- Remove transport.proto and move status codes into Status.java with a little refactoring to make
status easier & more precise to use
- Move DeferredProtoInputStream into a proto subpackage
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=76392172
diff --git a/core/src/main/java/com/google/net/stubby/Status.java b/core/src/main/java/com/google/net/stubby/Status.java
index 61f6ed7..31b2379 100644
--- a/core/src/main/java/com/google/net/stubby/Status.java
+++ b/core/src/main/java/com/google/net/stubby/Status.java
@@ -2,9 +2,11 @@
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
-import com.google.net.stubby.transport.Transport;
+import com.google.common.collect.Lists;
-import java.util.logging.Logger;
+import java.util.List;
+import java.util.Objects;
+import java.util.TreeMap;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@@ -13,16 +15,198 @@
* Defines the status of an operation using the canonical error space.
*/
@Immutable
-public class Status {
- public static final Status OK = new Status(Transport.Code.OK);
- public static final Status CANCELLED = new Status(Transport.Code.CANCELLED);
- public static final Metadata.Key<Transport.Code> CODE_KEY
- = Metadata.Key.of("grpc-status", new CodeMarshaller());
+public final class Status {
+
+ /**
+ * The set of canonical error codes. If new codes are added over time they must choose
+ * a numerical value that does not collide with any previously defined code.
+ */
+ public enum Code {
+ OK(0),
+
+ // The operation was cancelled (typically by the caller).
+ CANCELLED(1),
+
+ // Unknown error. An example of where this error may be returned is
+ // if a Status value received from another address space belongs to
+ // an error-space that is not known in this address space. Also
+ // errors raised by APIs that do not return enough error information
+ // may be converted to this error.
+ UNKNOWN(2),
+
+ // Client specified an invalid argument. Note that this differs
+ // from FAILED_PRECONDITION. INVALID_ARGUMENT indicates arguments
+ // that are problematic regardless of the state of the system
+ // (e.g., a malformed file name).
+ INVALID_ARGUMENT(3),
+
+ // Deadline expired before operation could complete. For operations
+ // that change the state of the system, this error may be returned
+ // even if the operation has completed successfully. For example, a
+ // successful response from a server could have been delayed long
+ // enough for the deadline to expire.
+ DEADLINE_EXCEEDED(4),
+
+ // Some requested entity (e.g., file or directory) was not found.
+ NOT_FOUND(5),
+
+ // Some entity that we attempted to create (e.g., file or directory)
+ // already exists.
+ ALREADY_EXISTS(6),
+
+ // The caller does not have permission to execute the specified
+ // operation. PERMISSION_DENIED must not be used for rejections
+ // caused by exhausting some resource (use RESOURCE_EXHAUSTED
+ // instead for those errors). PERMISSION_DENIED must not be
+ // used if the caller cannot be identified (use UNAUTHENTICATED
+ // instead for those errors).
+ PERMISSION_DENIED(7),
+
+ // Some resource has been exhausted, perhaps a per-user quota, or
+ // perhaps the entire file system is out of space.
+ RESOURCE_EXHAUSTED(8),
+
+ // Operation was rejected because the system is not in a state
+ // required for the operation's execution. For example, directory
+ // to be deleted may be non-empty, an rmdir operation is applied to
+ // a non-directory, etc.
+ //
+ // A litmus test that may help a service implementor in deciding
+ // between FAILED_PRECONDITION, ABORTED, and UNAVAILABLE:
+ // (a) Use UNAVAILABLE if the client can retry just the failing call.
+ // (b) Use ABORTED if the client should retry at a higher-level
+ // (e.g., restarting a read-modify-write sequence).
+ // (c) Use FAILED_PRECONDITION if the client should not retry until
+ // the system state has been explicitly fixed. E.g., if an "rmdir"
+ // fails because the directory is non-empty, FAILED_PRECONDITION
+ // should be returned since the client should not retry unless
+ // they have first fixed up the directory by deleting files from it.
+ FAILED_PRECONDITION(9),
+
+ // The operation was aborted, typically due to a concurrency issue
+ // like sequencer check failures, transaction aborts, etc.
+ //
+ // See litmus test above for deciding between FAILED_PRECONDITION,
+ // ABORTED, and UNAVAILABLE.
+ ABORTED(10),
+
+ // Operation was attempted past the valid range. E.g., seeking or
+ // reading past end of file.
+ //
+ // Unlike INVALID_ARGUMENT, this error indicates a problem that may
+ // be fixed if the system state changes. For example, a 32-bit file
+ // system will generate INVALID_ARGUMENT if asked to read at an
+ // offset that is not in the range [0,2^32-1], but it will generate
+ // OUT_OF_RANGE if asked to read from an offset past the current
+ // file size.
+ //
+ // There is a fair bit of overlap between FAILED_PRECONDITION and
+ // OUT_OF_RANGE. We recommend using OUT_OF_RANGE (the more specific
+ // error) when it applies so that callers who are iterating through
+ // a space can easily look for an OUT_OF_RANGE error to detect when
+ // they are done.
+ OUT_OF_RANGE(11),
+
+ // Operation is not implemented or not supported/enabled in this service.
+ UNIMPLEMENTED(12),
+
+ // Internal errors. Means some invariants expected by underlying
+ // system has been broken. If you see one of these errors,
+ // something is very broken.
+ INTERNAL(13),
+
+ // The service is currently unavailable. This is a most likely a
+ // transient condition and may be corrected by retrying with
+ // a backoff.
+ //
+ // See litmus test above for deciding between FAILED_PRECONDITION,
+ // ABORTED, and UNAVAILABLE.
+ UNAVAILABLE(14),
+
+ // Unrecoverable data loss or corruption.
+ DATA_LOSS(15),
+
+ // The request does not have valid authentication credentials for the
+ // operation.
+ UNAUTHENTICATED(16);
+
+ private final int value;
+
+ private Code(int value) {
+ this.value = value;
+ }
+
+ public int value() {
+ return value;
+ }
+
+ private Status status() {
+ return STATUS_LIST.get(value);
+ }
+ }
+
+ // Create the canonical list of Status instances indexed by their code values.
+ private static List<Status> STATUS_LIST;
+ static {
+ TreeMap<Integer, Status> canonicalizer = new TreeMap<>();
+ for (Code code : Code.values()) {
+ Status replaced = canonicalizer.put(code.value(), new Status(code));
+ if (replaced != null) {
+ throw new IllegalStateException("Code value duplication between " +
+ replaced.getCode().name() + " & " + code.name());
+ }
+ }
+ STATUS_LIST = Lists.newArrayList(canonicalizer.values());
+ }
+
+ // A pseudo-enum of Status instances mapped 1:1 with values in Code. This simplifies construction
+ // patterns for derived implementations of Status.
+ public static final Status OK = Code.OK.status();
+ public static final Status CANCELLED = Code.CANCELLED.status();
+ public static final Status UNKNOWN = Code.UNKNOWN.status();
+ public static final Status INVALID_ARGUMENT = Code.INVALID_ARGUMENT.status();
+ public static final Status DEADLINE_EXCEEDED = Code.DEADLINE_EXCEEDED.status();
+ public static final Status NOT_FOUND = Code.NOT_FOUND.status();
+ public static final Status ALREADY_EXISTS = Code.ALREADY_EXISTS.status();
+ public static final Status PERMISSION_DENIED = Code.PERMISSION_DENIED.status();
+ public static final Status UNAUTHENTICATED = Code.PERMISSION_DENIED.status();
+ public static final Status RESOURCE_EXHAUSTED = Code.RESOURCE_EXHAUSTED.status();
+ public static final Status FAILED_PRECONDITION =
+ Code.FAILED_PRECONDITION.status();
+ public static final Status ABORTED = Code.ABORTED.status();
+ public static final Status OUT_OF_RANGE = Code.OUT_OF_RANGE.status();
+ public static final Status UNIMPLEMENTED = Code.UNIMPLEMENTED.status();
+ public static final Status INTERNAL = Code.INTERNAL.status();
+ public static final Status UNAVAILABLE = Code.UNAVAILABLE.status();
+ public static final Status DATA_LOSS = Code.DATA_LOSS.status();
+
+ /**
+ * Return a {@link Status} given a canonical error {@link Code} value.
+ */
+ public static Status fromCodeValue(int codeValue) {
+ Status status;
+ if (codeValue < 0 || codeValue > STATUS_LIST.size()) {
+ return UNKNOWN.withDescription("Unknown code " + codeValue);
+ } else {
+ return status = STATUS_LIST.get(codeValue);
+ }
+ }
+
+ /**
+ * Key to bind status code to trailers.
+ */
+ public static final Metadata.Key<Status> CODE_KEY
+ = Metadata.Key.of("grpc-status", new StatusCodeMarshaller());
+
+ /**
+ * Key to bind status message to trailers.
+ */
public static final Metadata.Key<String> MESSAGE_KEY
= Metadata.Key.of("grpc-message", Metadata.STRING_MARSHALLER);
- private static final Logger log = Logger.getLogger(Status.class.getName());
-
+ /**
+ * Extract an error {@link Status} from the causal chain of a {@link Throwable}.
+ */
public static Status fromThrowable(Throwable t) {
for (Throwable cause : Throwables.getCausalChain(t)) {
if (cause instanceof OperationException) {
@@ -32,32 +216,44 @@
}
}
// Couldn't find a cause with a Status
- return new Status(Transport.Code.INTERNAL, t);
+ return INTERNAL.withCause(t);
}
- private final Transport.Code code;
+ private final Code code;
private final String description;
private final Throwable cause;
- public Status(Transport.Code code) {
+ private Status(Code code) {
this(code, null, null);
}
- public Status(Transport.Code code, @Nullable String description) {
- this(code, description, null);
- }
-
- public Status(Transport.Code code, @Nullable Throwable cause) {
- this(code, null, cause);
- }
-
- public Status(Transport.Code code, @Nullable String description, @Nullable Throwable cause) {
+ private Status(Code code, @Nullable String description, @Nullable Throwable cause) {
this.code = Preconditions.checkNotNull(code);
this.description = description;
this.cause = cause;
}
- public Transport.Code getCode() {
+ /**
+ * Create a derived instance of {@link Status} with the given cause.
+ */
+ public Status withCause(Throwable cause) {
+ if (Objects.equals(this.cause, cause)) {
+ return this;
+ }
+ return new Status(this.code, this.description, cause);
+ }
+
+ /**
+ * Create a derived instance of {@link Status} with the given description.
+ */
+ public Status withDescription(String description) {
+ if (Objects.equals(this.description, description)) {
+ return this;
+ }
+ return new Status(this.code, description, this.cause);
+ }
+
+ public Code getCode() {
return code;
}
@@ -71,25 +267,25 @@
return cause;
}
+ /**
+ * Is this status OK, i.e. not an error.
+ */
public boolean isOk() {
- return OK.getCode() == getCode();
+ return Code.OK == code;
}
/**
- * Override this status with another if allowed.
+ * Convert this {@link Status} to a {@link RuntimeException}. Use {@code #fromThrowable}
+ * to recover this {@link Status} instance when the returned exception is in the causal chain.
*/
- public Status overrideWith(Status newStatus) {
- if (this.getCode() == Transport.Code.OK || newStatus.code == Transport.Code.OK) {
- return this;
- } else {
- return newStatus;
- }
- }
-
public RuntimeException asRuntimeException() {
return new OperationRuntimeException(this);
}
+ /**
+ * Convert this {@link Status} to an {@link Exception}. Use {@code #fromThrowable}
+ * to recover this {@link Status} instance when the returned exception is in the causal chain.
+ */
public Exception asException() {
return new OperationException(this);
}
@@ -142,34 +338,25 @@
return builder.toString();
}
- private static class CodeMarshaller implements Metadata.Marshaller<Transport.Code> {
+ private static class StatusCodeMarshaller implements Metadata.Marshaller<Status> {
@Override
- public byte[] toBytes(Transport.Code value) {
- return Metadata.INTEGER_MARSHALLER.toBytes(value.getNumber());
+ public byte[] toBytes(Status status) {
+ return Metadata.INTEGER_MARSHALLER.toBytes(status.getCode().value());
}
@Override
- public String toAscii(Transport.Code value) {
- return Metadata.INTEGER_MARSHALLER.toAscii(value.getNumber());
+ public String toAscii(Status status) {
+ return Metadata.INTEGER_MARSHALLER.toAscii(status.getCode().value());
}
@Override
- public Transport.Code parseBytes(byte[] serialized) {
- return intToCode(Metadata.INTEGER_MARSHALLER.parseBytes(serialized));
+ public Status parseBytes(byte[] serialized) {
+ return fromCodeValue(Metadata.INTEGER_MARSHALLER.parseBytes(serialized));
}
@Override
- public Transport.Code parseAscii(String ascii) {
- return intToCode(Metadata.INTEGER_MARSHALLER.parseAscii(ascii));
- }
-
- private Transport.Code intToCode(Integer i) {
- Transport.Code code = Transport.Code.valueOf(i);
- if (code == null) {
- log.warning("Unknown Code: " + i);
- code = Transport.Code.UNKNOWN;
- }
- return code;
+ public Status parseAscii(String ascii) {
+ return fromCodeValue(Metadata.INTEGER_MARSHALLER.parseAscii(ascii));
}
}
}