blob: 0ec0997cad1a31862ffc4ad2684aa01f96b51ee6 [file] [log] [blame]
/*
* Copyright 2017 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.bookkeeper.client.api;
import java.lang.reflect.Field;
import java.util.function.Function;
import org.apache.bookkeeper.client.LedgerHandleAdv;
import org.apache.bookkeeper.common.annotation.InterfaceAudience.Public;
import org.apache.bookkeeper.common.annotation.InterfaceStability.Unstable;
/**
* Super class for all errors which occur using BookKeeper client.
*
* @since 4.6
*/
@Public
@Unstable
public class BKException extends Exception {
static final Function<Throwable, BKException> HANDLER = cause -> {
if (cause == null) {
return null;
}
if (cause instanceof BKException) {
return (BKException) cause;
} else {
BKException ex = new BKException(Code.UnexpectedConditionException);
ex.initCause(cause);
return ex;
}
};
protected final int code;
private static final LogMessagePool logMessagePool = new LogMessagePool();
/**
* Create a new exception.
*
* @param code the error code
*
* @see Code
*/
public BKException(int code) {
super(getMessage(code));
this.code = code;
}
/**
* Create a new exception with the <tt>cause</tt>.
*
* @param code exception code
* @param cause the exception cause
*/
public BKException(int code, Throwable cause) {
super(getMessage(code), cause);
this.code = code;
}
/**
* Get the return code for the exception.
*
* @return the error code
*
* @see Code
*/
public final int getCode() {
return this.code;
}
/**
* Returns a lazy error code formatter suitable to pass to log functions.
*
* @param code the error code value
*
* @return lazy error code log formatter
*/
public static Object codeLogger(int code) {
return logMessagePool.get(code);
}
/**
* Describe an error code.
*
* @param code the error code value
*
* @return the description of the error code
*/
public static String getMessage(int code) {
switch (code) {
case Code.OK:
return "No problem";
case Code.ReadException:
return "Error while reading ledger";
case Code.QuorumException:
return "Invalid quorum size on ensemble size";
case Code.NoBookieAvailableException:
return "No bookie available";
case Code.DigestNotInitializedException:
return "Digest engine not initialized";
case Code.DigestMatchException:
return "Entry digest does not match";
case Code.NotEnoughBookiesException:
return "Not enough non-faulty bookies available";
case Code.NoSuchLedgerExistsException:
return "No such ledger exists on Bookies";
case Code.NoSuchLedgerExistsOnMetadataServerException:
return "No such ledger exists on Metadata Server";
case Code.BookieHandleNotAvailableException:
return "Bookie handle is not available";
case Code.ZKException:
return "Error while using ZooKeeper";
case Code.MetaStoreException:
return "Error while using MetaStore";
case Code.LedgerExistException:
return "Ledger existed";
case Code.LedgerRecoveryException:
return "Error while recovering ledger";
case Code.LedgerClosedException:
return "Attempt to write to a closed ledger";
case Code.WriteException:
return "Write failed on bookie";
case Code.NoSuchEntryException:
return "No such entry";
case Code.IncorrectParameterException:
return "Incorrect parameter input";
case Code.InterruptedException:
return "Interrupted while waiting for permit";
case Code.ProtocolVersionException:
return "Bookie protocol version on server is incompatible with client";
case Code.MetadataVersionException:
return "Bad ledger metadata version";
case Code.DuplicateEntryIdException:
return "Attempted to add Duplicate entryId";
case Code.LedgerFencedException:
return "Ledger has been fenced off. Some other client must have opened it to read";
case Code.UnauthorizedAccessException:
return "Attempted to access ledger using the wrong password";
case Code.UnclosedFragmentException:
return "Attempting to use an unclosed fragment; This is not safe";
case Code.WriteOnReadOnlyBookieException:
return "Attempting to write on ReadOnly bookie";
case Code.TooManyRequestsException:
return "Too many requests to the same Bookie";
case Code.LedgerIdOverflowException:
return "Next ledgerID is too large.";
case Code.ReplicationException:
return "Errors in replication pipeline";
case Code.ClientClosedException:
return "BookKeeper client is closed";
case Code.IllegalOpException:
return "Invalid operation";
case Code.AddEntryQuorumTimeoutException:
return "Add entry quorum wait timed out";
case Code.TimeoutException:
return "Bookie operation timeout";
case Code.SecurityException:
return "Failed to establish a secure connection";
case Code.MetadataSerializationException:
return "Failed to serialize metadata";
default:
return "Unexpected condition";
}
}
/**
* Codes which represent the various exception types.
*/
public interface Code {
/** A placer holder (unused). */
int UNINITIALIZED = 1;
/** Everything is OK. */
int OK = 0;
/** Read operations failed (bookie error). */
int ReadException = -1;
/** Unused. */
int QuorumException = -2;
/** Unused. */
int NoBookieAvailableException = -3;
/** Digest Manager is not initialized (client error). */
int DigestNotInitializedException = -4;
/** Digest doesn't match on returned entries. */
int DigestMatchException = -5;
/** Not enough bookies available to form an ensemble. */
int NotEnoughBookiesException = -6;
/** No such ledger exists. */
int NoSuchLedgerExistsException = -7;
/** Bookies are not available. */
int BookieHandleNotAvailableException = -8;
/** ZooKeeper operations failed. */
int ZKException = -9;
/** Ledger recovery operations failed. */
int LedgerRecoveryException = -10;
/** Executing operations on a closed ledger handle. */
int LedgerClosedException = -11;
/** Write operations failed (bookie error). */
int WriteException = -12;
/** No such entry exists. */
int NoSuchEntryException = -13;
/** Incorrect parameters (operations are absolutely not executed). */
int IncorrectParameterException = -14;
/** Synchronous operations are interrupted. */
int InterruptedException = -15;
/** Protocol version is wrong (operations are absolutely not executed). */
int ProtocolVersionException = -16;
/** Bad version on executing metadata operations. */
int MetadataVersionException = -17;
/** Meta store operations failed. */
int MetaStoreException = -18;
/** Executing operations on a closed client. */
int ClientClosedException = -19;
/** Ledger already exists. */
int LedgerExistException = -20;
/**
* Add entry operation timeouts on waiting quorum responses.
*
* @since 4.5
*/
int AddEntryQuorumTimeoutException = -21;
/**
* Duplicated entry id is found when {@link LedgerHandleAdv#addEntry(long, byte[])}.
*
* @since 4.5
*/
int DuplicateEntryIdException = -22;
/**
* Operations timeouts.
*
* @since 4.5
*/
int TimeoutException = -23;
int SecurityException = -24;
/** No such ledger exists one metadata server. */
int NoSuchLedgerExistsOnMetadataServerException = -25;
/**
* Operation is illegal.
*/
int IllegalOpException = -100;
/**
* Operations failed due to ledgers are fenced.
*/
int LedgerFencedException = -101;
/**
* Operations failed due to unauthorized.
*/
int UnauthorizedAccessException = -102;
/**
* Replication failed due to unclosed fragments.
*/
int UnclosedFragmentException = -103;
/**
* Write operations failed due to bookies are readonly.
*/
int WriteOnReadOnlyBookieException = -104;
/**
* Operations failed due to too many requests in the queue.
*/
int TooManyRequestsException = -105;
/**
* Ledger id overflow happens on ledger manager.
*
* @since 4.5
*/
int LedgerIdOverflowException = -106;
/**
* Failure to serialize metadata.
*
* @since 4.9
*/
int MetadataSerializationException = -107;
/**
* Generic exception code used to propagate in replication pipeline.
*/
int ReplicationException = -200;
/**
* Unexpected condition.
*/
int UnexpectedConditionException = -999;
}
/**
* Code log message pool.
*/
private static class LogMessagePool {
private final int minCode;
private final String[] pool;
private LogMessagePool() {
Field[] fields = Code.class.getDeclaredFields();
this.minCode = minCode(fields);
this.pool = new String[-minCode + 2]; // UnexpectedConditionException is an outlier
initPoolMessages(fields);
}
private int minCode(Field[] fields) {
int min = 0;
for (Field field : fields) {
int code = getFieldInt(field);
if (code < min && code > Code.UnexpectedConditionException) {
min = code;
}
}
return min;
}
private void initPoolMessages(Field[] fields) {
for (Field field : fields) {
int code = getFieldInt(field);
int index = poolIndex(code);
if (index >= 0) {
pool[index] = String.format("%s: %s", field.getName(), getMessage(code));
}
}
}
private static int getFieldInt(Field field) {
try {
return field.getInt(null);
} catch (IllegalAccessException e) {
return -1;
}
}
private Object get(int code) {
int index = poolIndex(code);
String logMessage = index >= 0 ? pool[index] : null;
return logMessage != null ? logMessage : new UnrecognizedCodeLogFormatter(code);
}
private int poolIndex(int code) {
switch (code) {
case Code.UnexpectedConditionException:
return -minCode + 1;
default:
return code <= 0 && code >= minCode ? -minCode + code : -1;
}
}
/**
* Unrecognized code lazy log message formatter.
*/
private static class UnrecognizedCodeLogFormatter {
private final int code;
private UnrecognizedCodeLogFormatter(int code) {
this.code = code;
}
@Override
public String toString() {
return String.format("%d: %s", code, getMessage(code));
}
}
}
}