Skip to content

Commit

Permalink
Polishing #620
Browse files Browse the repository at this point in the history
Add Javadoc to exceptions. Extend ExceptionFactory usage.
  • Loading branch information
mp911de committed Oct 11, 2017
1 parent 6bd1eb7 commit e344a0a
Show file tree
Hide file tree
Showing 11 changed files with 189 additions and 32 deletions.
2 changes: 1 addition & 1 deletion src/main/java/io/lettuce/core/AbstractRedisClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ public void shutdown(long quietPeriod, long timeout, TimeUnit timeUnit) {
Thread.currentThread().interrupt();
throw new RedisCommandInterruptedException(e);
} catch (Exception e) {
throw new RedisCommandExecutionException(e);
throw ExceptionFactory.createExecutionException(null, e);
}
}

Expand Down
77 changes: 74 additions & 3 deletions src/main/java/io/lettuce/core/ExceptionFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@
*/
package io.lettuce.core;

import java.time.Duration;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField;

/**
* Factory for Redis exceptions.
*
Expand All @@ -23,9 +29,74 @@
*/
public abstract class ExceptionFactory {

private static final DateTimeFormatter MINUTES = new DateTimeFormatterBuilder().appendText(ChronoField.MINUTE_OF_DAY)
.appendLiteral(" minute(s)").toFormatter();

private static final DateTimeFormatter SECONDS = new DateTimeFormatterBuilder().appendText(ChronoField.SECOND_OF_DAY)
.appendLiteral(" second(s)").toFormatter();

private static final DateTimeFormatter MILLISECONDS = new DateTimeFormatterBuilder().appendText(ChronoField.MILLI_OF_DAY)
.appendLiteral(" millisecond(s)").toFormatter();

private ExceptionFactory() {
}

/**
* Create a {@link RedisCommandTimeoutException} with a detail message given the timeout.
*
* @param timeout the timeout value.
* @return the {@link RedisCommandTimeoutException}.
*/
public static RedisCommandTimeoutException createTimeoutException(Duration timeout) {
return new RedisCommandTimeoutException(String.format("Command timed out after %s", formatTimeout(timeout)));
}

/**
* Create a {@link RedisCommandTimeoutException} with a detail message given the message and timeout.
*
* @param message the detail message.
* @param timeout the timeout value.
* @return the {@link RedisCommandTimeoutException}.
*/
public static RedisCommandTimeoutException createTimeoutException(String message, Duration timeout) {
return new RedisCommandTimeoutException(
String.format("%s. Command timed out after %s", message, formatTimeout(timeout)));
}

static String formatTimeout(Duration duration) {

if (duration.isZero()) {
return "no timeout";
}

LocalTime time = LocalTime.MIDNIGHT.plus(duration);
if (isExactMinutes(duration)) {
return MINUTES.format(time);
}

if (isExactSeconds(duration)) {
return SECONDS.format(time);
}

if (isExactMillis(duration)) {
return MILLISECONDS.format(time);
}

return String.format("%d ns", duration.toNanos());
}

private static boolean isExactMinutes(Duration duration) {
return duration.toMillis() % (1000 * 60) == 0 && duration.getNano() == 0;
}

private static boolean isExactSeconds(Duration duration) {
return duration.toMillis() % (1000) == 0 && duration.getNano() == 0;
}

private static boolean isExactMillis(Duration duration) {
return duration.toNanos() % (1000 * 1000) == 0;
}

/**
* Create a {@link RedisCommandExecutionException} with a detail message. Specific Redis error messages may create subtypes
* of {@link RedisCommandExecutionException}.
Expand All @@ -38,11 +109,11 @@ public static RedisCommandExecutionException createExecutionException(String mes
}

/**
* Create a {@link RedisCommandExecutionException} with a detail message. Specific Redis error messages may create subtypes
* of {@link RedisCommandExecutionException}.
* Create a {@link RedisCommandExecutionException} with a detail message and optionally a {@link Throwable cause}. Specific
* Redis error messages may create subtypes of {@link RedisCommandExecutionException}.
*
* @param message the detail message.
* @param cause the nested exception.
* @param cause the nested exception, may be {@literal null}.
* @return the {@link RedisCommandExecutionException}.
*/
public static RedisCommandExecutionException createExecutionException(String message, Throwable cause) {
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/io/lettuce/core/LettuceFutures.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public static boolean awaitAll(long timeout, TimeUnit unit, Future<?>... futures
Thread.currentThread().interrupt();
throw new RedisCommandInterruptedException(e);
} catch (Exception e) {
throw new RedisCommandExecutionException(e);
throw ExceptionFactory.createExecutionException(null, e);
}
}

Expand All @@ -111,7 +111,7 @@ public static <T> T awaitOrCancel(RedisFuture<T> cmd, long timeout, TimeUnit uni
try {
if (!cmd.await(timeout, unit)) {
cmd.cancel(true);
throw new RedisCommandTimeoutException();
throw ExceptionFactory.createTimeoutException(Duration.ofNanos(unit.toNanos(timeout)));
}
return cmd.get();
} catch (RuntimeException e) {
Expand All @@ -128,7 +128,7 @@ public static <T> T awaitOrCancel(RedisFuture<T> cmd, long timeout, TimeUnit uni
Thread.currentThread().interrupt();
throw new RedisCommandInterruptedException(e);
} catch (Exception e) {
throw new RedisCommandExecutionException(e);
throw ExceptionFactory.createExecutionException(null, e);
}
}
}
6 changes: 3 additions & 3 deletions src/main/java/io/lettuce/core/PlainChannelInitializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@ static void pingBeforeActivate(AsyncCommand<?, ?, ?> cmd, CompletableFuture<Bool
if (cmd.isDone() || initializedFuture.isDone()) {
return;
}

initializedFuture.completeExceptionally(new RedisCommandTimeoutException(String.format(
"Cannot initialize channel (PING before activate) within %s", timeout)));
initializedFuture.completeExceptionally(ExceptionFactory.createTimeoutException(
"Cannot initialize channel (PING before activate)", timeout));
};

Timeout timeoutHandle = clientResources.timer().newTimeout(t -> {
Expand Down
29 changes: 23 additions & 6 deletions src/main/java/io/lettuce/core/RedisCommandExecutionException.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,33 @@
@SuppressWarnings("serial")
public class RedisCommandExecutionException extends RedisException {

public RedisCommandExecutionException(Throwable cause) {
super(cause);
}

/**
* Create a {@code RedisCommandExecutionException} with the specified detail message.
*
* @param msg the detail message.
*/
public RedisCommandExecutionException(String msg) {
super(msg);
}

public RedisCommandExecutionException(String msg, Throwable e) {
super(msg, e);
/**
* Create a {@code RedisCommandExecutionException} with the specified detail message and nested exception.
*
* @param msg the detail message.
* @param cause the nested exception.
*/
public RedisCommandExecutionException(String msg, Throwable cause) {
super(msg, cause);
}

/**
* Create a {@code RedisCommandExecutionException} with the specified nested exception.
*
* @param msg the detail message.
* @param cause the nested exception.
*/
public RedisCommandExecutionException(Throwable cause) {
super(cause);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2011-2016 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,11 +19,17 @@
* Exception thrown when the thread executing a redis command is interrupted.
*
* @author Will Glozer
* @author Mark Paluch
*/
@SuppressWarnings("serial")
public class RedisCommandInterruptedException extends RedisException {

public RedisCommandInterruptedException(Throwable e) {
super("Command interrupted", e);
/**
* Create a {@code RedisCommandInterruptedException} with the specified nested exception.
*
* @param cause the nested exception.
*/
public RedisCommandInterruptedException(Throwable cause) {
super("Command interrupted", cause);
}
}
10 changes: 9 additions & 1 deletion src/main/java/io/lettuce/core/RedisCommandTimeoutException.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2011-2016 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -23,10 +23,18 @@
@SuppressWarnings("serial")
public class RedisCommandTimeoutException extends RedisException {

/**
* Create a {@code RedisCommandTimeoutException} with a default message.
*/
public RedisCommandTimeoutException() {
super("Command timed out");
}

/**
* Create a {@code RedisCommandTimeoutException} with the specified detail message.
*
* @param msg the detail message.
*/
public RedisCommandTimeoutException(String msg) {
super(msg);
}
Expand Down
21 changes: 16 additions & 5 deletions src/main/java/io/lettuce/core/RedisConnectionException.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,31 @@
@SuppressWarnings("serial")
public class RedisConnectionException extends RedisException {

/**
* Create a {@code RedisConnectionException} with the specified detail message.
*
* @param msg the detail message.
*/
public RedisConnectionException(String msg) {
super(msg);
}

public RedisConnectionException(String msg, Throwable e) {
super(msg, e);
/**
* Create a {@code RedisConnectionException} with the specified detail message and nested exception.
*
* @param msg the detail message.
* @param cause the nested exception.
*/
public RedisConnectionException(String msg, Throwable cause) {
super(msg, cause);
}

/**
* Create a new {@link RedisConnectionException} given {@link SocketAddress} and the {@link Throwable cause}.
*
* @param remoteAddress
* @param cause
* @return
* @param remoteAddress remote socket address.
* @param cause the nested exception.
* @return the {@link RedisConnectionException}.
* @since 4.4
*/
public static RedisConnectionException create(SocketAddress remoteAddress, Throwable cause) {
Expand Down
20 changes: 19 additions & 1 deletion src/main/java/io/lettuce/core/RedisException.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2011-2016 the original author or authors.
* Copyright 2011-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,18 +19,36 @@
* Exception thrown when Redis returns an error message, or when the client fails for any reason.
*
* @author Will Glozer
* @author Mark Paluch
*/
@SuppressWarnings("serial")
public class RedisException extends RuntimeException {

/**
* Create a {@code RedisException} with the specified detail message.
*
* @param msg the detail message.
*/
public RedisException(String msg) {
super(msg);
}

/**
* Create a {@code RedisException} with the specified detail message and nested exception.
*
* @param msg the detail message.
* @param cause the nested exception.
*/
public RedisException(String msg, Throwable e) {
super(msg, e);
}

/**
* Create a {@code RedisException} with the specified nested exception.
*
* @param msg the detail message.
* @param cause the nested exception.
*/
public RedisException(Throwable cause) {
super(cause);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ private Object getExecutions(Map<RedisClusterNode, Object> executions) throws Ex
if (executionModel == ExecutionModel.SYNC) {

if (!awaitAll(timeout.toNanos(), TimeUnit.NANOSECONDS, asyncExecutions.values())) {
throw createTimeoutException(asyncExecutions);
throw createTimeoutException(asyncExecutions, timeout);
}

if (atLeastOneFailed(asyncExecutions)) {
Expand Down Expand Up @@ -210,7 +210,8 @@ private boolean atLeastOneFailed(Map<RedisClusterNode, CompletionStage<?>> execu
.anyMatch(completionStage -> completionStage.toCompletableFuture().isCompletedExceptionally());
}

private RedisCommandTimeoutException createTimeoutException(Map<RedisClusterNode, CompletionStage<?>> executions) {
private RedisCommandTimeoutException createTimeoutException(Map<RedisClusterNode, CompletionStage<?>> executions,
Duration timeout) {

List<RedisClusterNode> notFinished = new ArrayList<>();
executions.forEach((redisClusterNode, completionStage) -> {
Expand All @@ -220,7 +221,7 @@ private RedisCommandTimeoutException createTimeoutException(Map<RedisClusterNode
});

String description = getNodeDescription(notFinished);
return new RedisCommandTimeoutException("Command timed out for node(s): " + description);
return ExceptionFactory.createTimeoutException("Command timed out for node(s): " + description, timeout);
}

private RedisCommandExecutionException createExecutionException(Map<RedisClusterNode, CompletionStage<?>> executions) {
Expand Down
31 changes: 28 additions & 3 deletions src/test/java/io/lettuce/core/ExceptionFactoryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@

import static org.assertj.core.api.Assertions.assertThat;

import io.lettuce.core.ExceptionFactory;
import io.lettuce.core.RedisBusyException;
import io.lettuce.core.RedisNoScriptException;
import java.time.Duration;

import org.junit.Test;

/**
Expand Down Expand Up @@ -58,4 +57,30 @@ public void shouldCreateExecutionException() {
assertThat(ExceptionFactory.createExecutionException(null, new IllegalStateException())).isInstanceOf(
RedisCommandExecutionException.class).hasRootCauseInstanceOf(IllegalStateException.class);
}

@Test
public void shouldFormatExactUnits() {

assertThat(ExceptionFactory.formatTimeout(Duration.ofMinutes(2))).isEqualTo("2 minute(s)");
assertThat(ExceptionFactory.formatTimeout(Duration.ofMinutes(1))).isEqualTo("1 minute(s)");
assertThat(ExceptionFactory.formatTimeout(Duration.ofMinutes(0))).isEqualTo("no timeout");

assertThat(ExceptionFactory.formatTimeout(Duration.ofSeconds(2))).isEqualTo("2 second(s)");
assertThat(ExceptionFactory.formatTimeout(Duration.ofSeconds(1))).isEqualTo("1 second(s)");
assertThat(ExceptionFactory.formatTimeout(Duration.ofSeconds(0))).isEqualTo("no timeout");

assertThat(ExceptionFactory.formatTimeout(Duration.ofMillis(2))).isEqualTo("2 millisecond(s)");
assertThat(ExceptionFactory.formatTimeout(Duration.ofMillis(1))).isEqualTo("1 millisecond(s)");
assertThat(ExceptionFactory.formatTimeout(Duration.ofMillis(0))).isEqualTo("no timeout");
}

@Test
public void shouldFormatToMinmalApplicableTimeunit() {

assertThat(ExceptionFactory.formatTimeout(Duration.ofMinutes(2).plus(Duration.ofSeconds(10)))).isEqualTo(
"130 second(s)");
assertThat(ExceptionFactory.formatTimeout(Duration.ofSeconds(2).plus(Duration.ofMillis(5)))).isEqualTo(
"2005 millisecond(s)");
assertThat(ExceptionFactory.formatTimeout(Duration.ofNanos(2))).isEqualTo("2 ns");
}
}

0 comments on commit e344a0a

Please sign in to comment.