Skip to content

Commit

Permalink
Refactor exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
olim7t committed May 31, 2017
1 parent d3334b0 commit 556a800
Show file tree
Hide file tree
Showing 20 changed files with 225 additions and 68 deletions.
Expand Up @@ -15,16 +15,28 @@
*/
package com.datastax.oss.driver.api.core;

public class DriverException extends RuntimeException {
public DriverException(String message) {
/**
* Base class for all exceptions thrown by the driver.
*
* <p>Note that, for obvious programming errors (for example, calling {@link Cluster#connect()} on a
* cluster instance that was previously closed), the driver might throw JDK runtime exceptions, such
* as {@link IllegalArgumentException} or {@link IllegalStateException}. In all other cases, it will
* be an instance of this class.
*
* <p>One special case is when the driver tried multiple nodes to complete a request, and they all
* failed; the error returned to the client will be an {@link AllNodesFailedException}, which wraps
* a map of errors per node.
*/
public abstract class DriverException extends RuntimeException {
protected DriverException(String message) {
super(message);
}

public DriverException(String message, Throwable cause) {
protected DriverException(String message, Throwable cause) {
super(message, cause);
}

public DriverException(Throwable cause) {
protected DriverException(Throwable cause) {
super(cause);
}
}
@@ -0,0 +1,29 @@
/*
* Copyright (C) 2017-2017 DataStax Inc.
*
* 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 com.datastax.oss.driver.api.core;

/**
* Thrown by synchronous wrapper methods (such as {@link Cluster#connect()}, when the underlying
* future was completed with a <em>checked</em> exception.
*
* <p>This exception should be rarely thrown (if ever). Most of the time, the driver uses unchecked
* exceptions, which will be rethrown directly instead of being wrapped in this class.
*/
public class DriverExecutionException extends DriverException {
public DriverExecutionException(Throwable cause) {
super(cause);
}
}
Expand Up @@ -13,15 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.datastax.oss.driver.api.core.connection;
package com.datastax.oss.driver.api.core;

/** A generic error on a connection to a node. */
public class ConnectionException extends RuntimeException {
public ConnectionException(String message) {
/** Thrown when a driver request timed out. */
public class DriverTimeoutException extends DriverException {
public DriverTimeoutException(String message) {
super(message);
}

public ConnectionException(String message, Throwable cause) {
super(message, cause);
}
}
Expand Up @@ -23,8 +23,12 @@
/**
* Indicates that we've attempted to connect to a Cassandra node with a protocol version that it
* cannot handle (e.g., connecting to a C* 2.1 node with protocol version 4).
*
* <p>The only time when this is returned directly to the client (wrapped in a {@link
* AllNodesFailedException}) is at initialization. If it happens later when the driver is already
* connected, it is just logged an the corresponding node is forced down.
*/
public class UnsupportedProtocolVersionException extends RuntimeException {
public class UnsupportedProtocolVersionException extends DriverException {
private static final long serialVersionUID = 0;

private final SocketAddress address;
Expand Down
Expand Up @@ -15,9 +15,16 @@
*/
package com.datastax.oss.driver.api.core.auth;

import com.datastax.oss.driver.api.core.AllNodesFailedException;
import java.net.SocketAddress;

/** Indicates an error during the authentication phase while connecting to a node. */
/**
* Indicates an error during the authentication phase while connecting to a node.
*
* <p>The only time when this is returned directly to the client (wrapped in a {@link
* AllNodesFailedException}) is at initialization. If it happens later when the driver is already
* connected, it is just logged and the connection will be reattempted.
*/
public class AuthenticationException extends RuntimeException {
private static final long serialVersionUID = 0;

Expand All @@ -31,4 +38,9 @@ public AuthenticationException(SocketAddress address, String message, Throwable
super(String.format("Authentication error on host %s: %s", address, message), cause);
this.address = address;
}

/** The address of the node that encountered the error. */
public SocketAddress getAddress() {
return address;
}
}
Expand Up @@ -15,16 +15,27 @@
*/
package com.datastax.oss.driver.api.core.connection;

import com.datastax.oss.driver.api.core.AllNodesFailedException;
import com.datastax.oss.driver.api.core.DriverException;

/**
* Indicates that a write was attempted on a connection that already handles too many simultaneous
* requests.
*
* <p>This might happen under heavy load. The driver will automatically try the next node in the
* query plan. Therefore the only way that the client can observe this exception is as part of a
* {@link AllNodesFailedException}.
*/
public class BusyConnectionException extends ConnectionException {
public class BusyConnectionException extends DriverException {

public BusyConnectionException(int maxAvailableIds) {
super(
String.format(
"" + "Connection has exceeded its maximum of %d simultaneous requests",
maxAvailableIds));
"Connection has exceeded its maximum of %d simultaneous requests", maxAvailableIds));
}

@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
}
@@ -0,0 +1,39 @@
/*
* Copyright (C) 2017-2017 DataStax Inc.
*
* 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 com.datastax.oss.driver.api.core.connection;

import com.datastax.oss.driver.api.core.AllNodesFailedException;
import com.datastax.oss.driver.api.core.DriverException;

/**
* Thrown when the connection on which a request was executing is closed due to an unrelated event.
*
* <p>For example, this can happen if the node is unresponsive and a heartbeat query failed, or if
* the node was forced down.
*
* <p>The driver will always retry these requests on the next node transparently. Therefore, the
* only way to observe this exception is as part of an {@link AllNodesFailedException}.
*/
public class ClosedConnectionException extends DriverException {

public ClosedConnectionException(String message) {
super(message);
}

public ClosedConnectionException(String message, Throwable cause) {
super(message, cause);
}
}
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2017-2017 DataStax Inc.
*
* 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 com.datastax.oss.driver.api.core.connection;

import com.datastax.oss.driver.api.core.AllNodesFailedException;
import com.datastax.oss.driver.api.core.DriverException;

/**
* Indicates a generic error while initializing a connection.
*
* <p>The only time when this is returned directly to the client (wrapped in a {@link
* AllNodesFailedException}) is at initialization. If it happens later when the driver is already
* connected, it is just logged an the connection is reattempted.
*/
public class ConnectionInitException extends DriverException {
public ConnectionInitException(String message, Throwable cause) {
super(message, cause);
}
}
Expand Up @@ -15,13 +15,28 @@
*/
package com.datastax.oss.driver.api.core.connection;

public class HeartbeatException extends ConnectionException {
import com.datastax.oss.driver.api.core.DriverException;
import java.net.SocketAddress;

public HeartbeatException(String message) {
super(message);
}
/**
* Thrown when a heartbeat query fails.
*
* <p>Heartbeat queries are sent automatically on idle connections, to ensure that they are still
* alive. If a heartbeat query fails, the connection is closed, and all pending queries are aborted.
* Depending on the retry policy, the heartbeat exception can either be rethrown directly to the
* client, or the driver tries the next host in the query plan.
*/
public class HeartbeatException extends DriverException {

public HeartbeatException(String message, Throwable cause) {
private final SocketAddress address;

public HeartbeatException(SocketAddress address, String message, Throwable cause) {
super(message, cause);
this.address = address;
}

/** The address of the node that encountered the error. */
public SocketAddress getAddress() {
return address;
}
}
Expand Up @@ -171,7 +171,7 @@ private void init() {
private void connect(CqlIdentifier keyspace, CompletableFuture<CqlSession> connectFuture) {
assert adminExecutor.inEventLoop();
if (closeWasCalled) {
connectFuture.completeExceptionally(new DriverException("Cluster was closed"));
connectFuture.completeExceptionally(new IllegalStateException("Cluster was closed"));
} else {
DefaultSession.init(context, keyspace)
.whenCompleteAsync(
Expand All @@ -180,7 +180,8 @@ private void connect(CqlIdentifier keyspace, CompletableFuture<CqlSession> conne
connectFuture.completeExceptionally(error);
} else if (closeWasCalled) {
connectFuture.completeExceptionally(
new DriverException("Cluster was closed while session was initializing"));
new IllegalStateException(
"Cluster was closed while session was initializing"));
session.forceCloseAsync();
} else {
sessions.add(session);
Expand Down
Expand Up @@ -15,7 +15,7 @@
*/
package com.datastax.oss.driver.internal.core.adminrequest;

import com.datastax.oss.driver.api.core.DriverException;
import com.datastax.oss.driver.api.core.DriverTimeoutException;
import com.datastax.oss.driver.api.core.ProtocolVersion;
import com.datastax.oss.driver.api.type.codec.TypeCodecs;
import com.datastax.oss.driver.internal.core.channel.DriverChannel;
Expand Down Expand Up @@ -44,7 +44,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/** Handles the lifecyle of an admin request. */
/** Handles the lifecyle of an admin request (such as a node refresh or schema refresh query). */
public class AdminRequestHandler implements ResponseCallback {
private static final Logger LOG = LoggerFactory.getLogger(AdminRequestHandler.class);

Expand Down Expand Up @@ -109,7 +109,8 @@ private void onWriteComplete(Future<? super Void> future) {
}

private void fireTimeout() {
result.completeExceptionally(new DriverException("Query timed out"));
result.completeExceptionally(
new DriverTimeoutException(String.format("%s timed out after %s", debugString, timeout)));
}

@Override
Expand Down Expand Up @@ -137,7 +138,10 @@ public void onResponse(Frame responseFrame) {
result.complete(null);
} else {
result.completeExceptionally(
new DriverException("Unexpected response to control query: " + message));
// The actual exception type does not really matters, this is only logged, never
// returned to the client
new IllegalArgumentException(
String.format("%s got unexpected response %s", debugString, message)));
}
}

Expand Down
Expand Up @@ -15,6 +15,7 @@
*/
package com.datastax.oss.driver.internal.core.channel;

import com.datastax.oss.driver.api.core.DriverTimeoutException;
import com.datastax.oss.driver.internal.core.util.ProtocolUtils;
import com.datastax.oss.protocol.internal.Frame;
import com.datastax.oss.protocol.internal.Message;
Expand All @@ -25,18 +26,17 @@
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/** Common infrastructure to send a native protocol request from a channel handler. */
abstract class InternalRequest implements ResponseCallback {
abstract class ChannelHandlerRequest implements ResponseCallback {

final Channel channel;
final ChannelHandlerContext ctx;
private final long timeoutMillis;

private ScheduledFuture<?> timeoutFuture;

InternalRequest(ChannelHandlerContext ctx, long timeoutMillis) {
ChannelHandlerRequest(ChannelHandlerContext ctx, long timeoutMillis) {
this.ctx = ctx;
this.channel = ctx.channel();
this.timeoutMillis = timeoutMillis;
Expand All @@ -51,10 +51,6 @@ abstract class InternalRequest implements ResponseCallback {
/** either message or cause can be null */
abstract void fail(String message, Throwable cause);

void fail(String message) {
fail(message, null);
}

void fail(Throwable cause) {
fail(null, cause);
}
Expand Down Expand Up @@ -89,21 +85,23 @@ public final void onFailure(Throwable error) {
}

private void onTimeout() {
fail(new TimeoutException(describe() + ": timed out after " + timeoutMillis + " ms"));
fail(new DriverTimeoutException(describe() + ": timed out after " + timeoutMillis + " ms"));
}

void failOnUnexpected(Message response) {
if (response instanceof Error) {
Error error = (Error) response;
fail(
String.format(
"%s: unexpected server error [%s] %s",
describe(), ProtocolUtils.errorCodeString(error.code), error.message));
new IllegalArgumentException(
String.format(
"%s: unexpected server error [%s] %s",
describe(), ProtocolUtils.errorCodeString(error.code), error.message)));
} else {
fail(
String.format(
"%s: unexpected server response opcode=%s",
describe(), ProtocolUtils.opcodeString(response.opcode)));
new IllegalArgumentException(
String.format(
"%s: unexpected server response opcode=%s",
describe(), ProtocolUtils.opcodeString(response.opcode))));
}
}
}

0 comments on commit 556a800

Please sign in to comment.