blob: 5c08a384000b63d575e890416fe85fb27c0bb5a3 [file] [log] [blame]
// Copyright 2016 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package io.v.syncbase.exception;
import java.util.NoSuchElementException;
import java.util.concurrent.CancellationException;
import io.v.syncbase.core.VError;
import io.v.v23.verror.VException;
import io.v.v23.verror.VException.ActionCode;
import static io.v.v23.verror.VException.ActionCode.fromValue;
/**
* Utility for exception chaining.
*/
public final class Exceptions {
private Exceptions() {
}
private static String baseName(String v23ErrorId) {
String[] tokens = v23ErrorId.split("\\.");
int n = tokens.length;
if (n < 1) {
return v23ErrorId;
}
return tokens[n - 1];
}
private static void chainThrow(String message, String goErrorId, ActionCode action, Exception
cause)
throws SyncbaseException {
if (goErrorId == null) {
goErrorId = "null";
}
String fullMessage = message + ": " + baseName(goErrorId);
switch (goErrorId) {
case "v.io/v23/verror.NotImplemented":
throw new UnsupportedOperationException(fullMessage, cause);
case "v.io/v23/verror.EndOfFile":
throw new SyncbaseEndOfFileException(fullMessage, cause);
case "v.io/v23/verror.BadArg":
case "v.io/v23/services/syncbase.InvalidName":
case "v.io/v23/services/syncbase.NotBoundToBatch":
case "v.io/v23/services/syncbase.ReadOnlyBatch":
throw new IllegalArgumentException(fullMessage, cause);
case "v.io/v23/verror.Exist":
case "v.io/v23/services/syncbase.NotInDevMode":
case "v.io/v23/services/syncbase.BlobNotCommitted":
case "v.io/v23/services/syncbase.InvalidPermissionsChange":
case "v.io/v23/verror.Aborted":
throw new IllegalStateException(fullMessage, cause);
case "v.io/v23/verror.NoExist":
throw withCause(new NoSuchElementException(fullMessage), cause);
case "v.io/v23/verror.Unknown":
case "v.io/v23/verror.Internal":
case "v.io/v23/verror.BadState":
case "v.io/v23/verror.UnknownMethod":
case "v.io/v23/verror.UnknownSuffix":
case "v.io/v23/verror.BadProtocol":
case "v.io/v23/services/syncbase.BadExecStreamHeader":
throw new SyncbaseInternalException(fullMessage, cause);
case "v.io/v23/verror.NoServers":
case "v.io/v23/services/syncbase.SyncgroupJoinFailed":
throw new SyncbaseNoServersException(fullMessage, cause);
case "v.io/v23/verror.Canceled":
throw withCause(new CancellationException(fullMessage), cause);
case "v.io/v23/verror.Timeout":
throw new SyncbaseRetryBackoffException(fullMessage, cause);
case "v.io/v23/services/syncbase.CorruptDatabase":
throw new SyncbaseRestartException(fullMessage, cause);
case "v.io/v23/verror.BadVersion":
case "v.io/v23/services/syncbase.ConcurrentBatch":
case "v.io/v23/services/syncbase.UnknownBatch":
throw new SyncbaseRaceException(fullMessage, cause);
case "v.io/v23/verror.NoAccess":
case "v.io/v23/verror.NotTrusted":
case "v.io/v23/verror.NoExistOrNoAccess":
case "v.io/v23/services/syncbase.UnauthorizedCreateId":
case "v.io/v23/services/syncbase.InferAppBlessingFailed":
case "v.io/v23/services/syncbase.InferUserBlessingFailed":
case "v.io/v23/services/syncbase.InferDefaultPermsFailed":
throw new SyncbaseSecurityException(fullMessage, cause);
default:
String fullerMessage = fullMessage + " (unexpected error ID " + goErrorId + ")";
// See https://godoc.org/v.io/v23/verror#ActionCode
switch (action) {
case RETRY_REFETCH:
throw new SyncbaseRetryRefetchException(fullerMessage, cause);
case RETRY_BACKOFF:
throw new SyncbaseRetryBackoffException(fullerMessage, cause);
case RETRY_CONNECTION:
throw new SyncbaseRetryConnectionException(fullerMessage, cause);
case NO_RETRY:
default:
throw new SyncbaseInternalException(fullerMessage, cause);
}
}
}
private static void chainThrow(String javaMessage, String goMessage, String v23ErrorId,
ActionCode action, Exception cause) throws SyncbaseException {
chainThrow("while " + javaMessage + " got error " + goMessage, v23ErrorId, action, cause);
}
/**
* Throw an exception that wraps a low-level exception.
*
* @param javaMessage gives context from where the low-level exception was caught
* @param cause the low-level exception, possibly originating in native code
* @throws SyncbaseException always
*/
public static void chainThrow(String javaMessage, VError cause) throws SyncbaseException {
ActionCode action = fromValue((int) cause.actionCode);
chainThrow(javaMessage, cause.message, cause.id, action, cause);
}
/**
* Throw an exception that wraps a low-level exception.
*
* @param javaMessage gives context from where the low-level exception was caught
* @param cause the low-level exception, possibly originating in native code
* @throws SyncbaseException always
*/
public static void chainThrow(String javaMessage, VException cause) throws SyncbaseException {
chainThrow(javaMessage, cause.getMessage(), cause.getID(), cause.getAction(),
cause);
}
/**
* Throw an exception that wraps a low-level exception.
*
* @param doing what the high-level code was doing when the exception was caught
* @param name a Vanadoum name, i.e the name field of an Id object
* @param cause the low-level exception, possibly originating in native code
* @throws SyncbaseException always
*/
public static void chainThrow(String doing, String name, VError cause) throws
SyncbaseException {
chainThrow(doing + " " + name, cause);
}
/**
* Throw an exception that wraps a low-level exception.
*
* @param doing what the high-level code was doing when the exception was caught
* @param name a Vanadoum name, i.e the name field of an Id object
* @param cause the low-level exception, possibly originating in native code
* @throws SyncbaseException always
*/
public static void chainThrow(String doing, String name, VException cause) throws
SyncbaseException {
chainThrow(doing + " " + name, cause);
}
private static <T extends Exception> T withCause(T e, Exception cause) {
e.initCause(cause);
return e;
}
}