From d5ab7877f2ddf9254035f409f46f3f0248fa0eb8 Mon Sep 17 00:00:00 2001 From: Lucas Pedroza Date: Tue, 12 Nov 2024 17:07:23 +0100 Subject: [PATCH] Fix checkstyle issues in com.fauna.client --- .../com/fauna/client/BaseFaunaClient.java | 6 +- .../client/ExponentialBackoffStrategy.java | 126 ++++++----- src/main/java/com/fauna/client/Fauna.java | 17 +- .../java/com/fauna/client/FaunaClient.java | 203 +++++++++++------- .../java/com/fauna/client/FaunaConfig.java | 29 +-- src/main/java/com/fauna/client/FaunaRole.java | 22 +- .../java/com/fauna/client/FaunaScope.java | 60 +++++- src/main/java/com/fauna/client/Logging.java | 17 +- .../com/fauna/client/NoRetryStrategy.java | 10 +- .../java/com/fauna/client/PageIterator.java | 71 ++++-- .../java/com/fauna/client/RequestBuilder.java | 152 +++++++++---- .../java/com/fauna/client/RetryHandler.java | 56 ++++- .../com/fauna/client/ScopedFaunaClient.java | 53 ++++- .../java/com/fauna/client/StatsCollector.java | 6 +- .../com/fauna/client/StatsCollectorImpl.java | 8 +- 15 files changed, 576 insertions(+), 260 deletions(-) diff --git a/src/main/java/com/fauna/client/BaseFaunaClient.java b/src/main/java/com/fauna/client/BaseFaunaClient.java index 911ac7e1..b1d74bce 100644 --- a/src/main/java/com/fauna/client/BaseFaunaClient.java +++ b/src/main/java/com/fauna/client/BaseFaunaClient.java @@ -24,8 +24,8 @@ public final class BaseFaunaClient extends FaunaClient { * @param httpClient A Java HTTP client instance. * @param retryStrategy An implementation of RetryStrategy. */ - public BaseFaunaClient(FaunaConfig faunaConfig, - HttpClient httpClient, RetryStrategy retryStrategy) { + public BaseFaunaClient(final FaunaConfig faunaConfig, + final HttpClient httpClient, final RetryStrategy retryStrategy) { super(faunaConfig.getSecret(), faunaConfig.getLogHandler(), faunaConfig.getStatsCollector()); this.httpClient = httpClient; @@ -52,7 +52,7 @@ public BaseFaunaClient(FaunaConfig faunaConfig, * * @param faunaConfig The Fauna configuration settings. */ - public BaseFaunaClient(FaunaConfig faunaConfig) { + public BaseFaunaClient(final FaunaConfig faunaConfig) { this(faunaConfig, HttpClient.newBuilder().build(), DEFAULT_RETRY_STRATEGY); } diff --git a/src/main/java/com/fauna/client/ExponentialBackoffStrategy.java b/src/main/java/com/fauna/client/ExponentialBackoffStrategy.java index 742c0954..81280003 100644 --- a/src/main/java/com/fauna/client/ExponentialBackoffStrategy.java +++ b/src/main/java/com/fauna/client/ExponentialBackoffStrategy.java @@ -1,7 +1,10 @@ package com.fauna.client; - -public class ExponentialBackoffStrategy implements RetryStrategy { +/** + * Implements an exponential backoff strategy for retries. + * The backoff delay increases exponentially with each retry attempt, with optional jitter. + */ +public final class ExponentialBackoffStrategy implements RetryStrategy { private final float backoffFactor; private final int maxAttempts; private final int initialIntervalMillis; @@ -9,43 +12,34 @@ public class ExponentialBackoffStrategy implements RetryStrategy { private final float jitterFactor; /** - * Construct an Exponential backoff strategy. - * The basic formula for exponential backoff is `b^(a-1)` where `b` is the backoff factor, and `a` is the retry - * attempt number. So for a backoff factor of 2, you get: - * 2^0=1, 2^1=2, 2^3=4, 2^4=8 ... + * Constructs an Exponential backoff strategy. * - * @param maxAttempts The maximum amount of retry attempts. Defaults to 3 retry attempts which means - * the client will make a total of 4 requests before giving up. - * @param backoffFactor Defines how quickly the client will back off, default is 2. - * A value of 1 would not backoff (not recommended). - * @param initialIntervalMillis Defines the interval for the first wait. Default is 1000ms. - * @param maxBackoffMillis Set a cap on the delay between requests. The default is 20,000ms - * @param jitterFactor A value between 0 (0%) and 1 (100%) that controls how much to jitter the delay. - * The default is 0.5. + * @param maxAttempts The maximum number of retry attempts. Defaults to 3 retries. + * @param backoffFactor The factor by which the delay will increase. Default is 2. + * @param initialIntervalMillis The interval (in milliseconds) for the first retry attempt. Default is 1000ms. + * @param maxBackoffMillis The maximum delay (in milliseconds) between retries. Default is 20000ms. + * @param jitterFactor A value between 0 and 1 that controls the jitter factor. Default is 0.5. */ - ExponentialBackoffStrategy(int maxAttempts, float backoffFactor, - int initialIntervalMillis, - int maxBackoffMillis, float jitterFactor) { + ExponentialBackoffStrategy(final int maxAttempts, final float backoffFactor, + final int initialIntervalMillis, + final int maxBackoffMillis, final float jitterFactor) { this.maxAttempts = maxAttempts; this.backoffFactor = backoffFactor; this.initialIntervalMillis = initialIntervalMillis; this.maxBackoffMillis = maxBackoffMillis; this.jitterFactor = jitterFactor; + if (jitterFactor < 0.0 || jitterFactor > 1.0) { - throw new IllegalArgumentException( - "Jitter factor must be between 0 and 1."); + throw new IllegalArgumentException("Jitter factor must be between 0 and 1."); } if (backoffFactor < 0.0) { - throw new IllegalArgumentException( - "Backoff factor must be positive."); + throw new IllegalArgumentException("Backoff factor must be positive."); } if (maxAttempts < 0) { - throw new IllegalArgumentException( - "Max attempts must be a natural number (not negative)."); + throw new IllegalArgumentException("Max attempts must be a natural number (not negative)."); } if (initialIntervalMillis < 0) { - throw new IllegalArgumentException( - "Initial interval must be positive."); + throw new IllegalArgumentException("Initial interval must be positive."); } if (maxBackoffMillis < 0) { throw new IllegalArgumentException("Max backoff must be positive."); @@ -53,36 +47,31 @@ public class ExponentialBackoffStrategy implements RetryStrategy { } /** - * Get the % to jitter the backoff, will be a value between 0 and jitterFactor. + * Generates a random jitter percent between 0 and the jitterFactor. * - * @return A randomly generated value between 0 and jitterFactor. + * @return A random jitter percent. */ private double getJitterPercent() { return Math.random() * jitterFactor; } @Override - public boolean canRetry(int retryAttempt) { + public boolean canRetry(final int retryAttempt) { if (retryAttempt < 0) { - throw new IllegalArgumentException( - "Retry attempt must be a natural number (not negative)."); + throw new IllegalArgumentException("Retry attempt must be a natural number (not negative)."); } return retryAttempt <= maxAttempts; } @Override - public int getDelayMillis(int retryAttempt) { + public int getDelayMillis(final int retryAttempt) { if (retryAttempt < 0) { - throw new IllegalArgumentException( - "Retry attempt must be a natural number (not negative)."); + throw new IllegalArgumentException("Retry attempt must be a natural number (not negative)."); } else if (retryAttempt == 0) { return 0; } else { - double deterministicBackoff = - Math.pow(this.backoffFactor, retryAttempt - 1); - double calculatedBackoff = - deterministicBackoff * (1 - getJitterPercent()) * - initialIntervalMillis; + double deterministicBackoff = Math.pow(this.backoffFactor, retryAttempt - 1); + double calculatedBackoff = deterministicBackoff * (1 - getJitterPercent()) * initialIntervalMillis; return (int) Math.min(calculatedBackoff, this.maxBackoffMillis); } } @@ -92,47 +81,77 @@ public int getMaxRetryAttempts() { return this.maxAttempts; } - /** - * Build a new ExponentialBackoffStrategy. This builder only supports setting maxAttempts, because that's the only - * variable that we recommend users change in production. If you need to modify other values for debugging, or other - * purposes, then you can use the constructor directly. + * Builder class for the ExponentialBackoffStrategy. + * Allows fluent configuration of the backoff strategy parameters. */ public static class Builder { private float backoffFactor = 2.0f; - // Results in delay of 1, 2, 4, 8, 16... seconds. - private int maxAttempts = 3; // Limits number of retry attempts. + private int maxAttempts = 3; private int initialIntervalMillis = 1000; private int maxBackoffMillis = 20_000; - // A jitterFactor of 0.5, combined with a backoffFactor of 2 ensures that the delay is always increasing. private float jitterFactor = 0.5f; - - public Builder maxAttempts(int maxAttempts) { + /** + * Sets the maximum number of retry attempts. + * + * @param maxAttempts The maximum number of retry attempts. + * @return The current Builder instance. + */ + public Builder maxAttempts(final int maxAttempts) { this.maxAttempts = maxAttempts; return this; } - public Builder backoffFactor(float backoffFactor) { + /** + * Sets the backoff factor. + * + * @param backoffFactor The factor by which the backoff delay increases. + * @return The current Builder instance. + */ + public Builder backoffFactor(final float backoffFactor) { this.backoffFactor = backoffFactor; return this; } - public Builder initialIntervalMillis(int initialIntervalMillis) { + /** + * Sets the initial interval (in milliseconds) for the first retry attempt. + * + * @param initialIntervalMillis The initial interval in milliseconds. + * @return The current Builder instance. + */ + public Builder initialIntervalMillis(final int initialIntervalMillis) { this.initialIntervalMillis = initialIntervalMillis; return this; } - public Builder maxBackoffMillis(int maxBackoffMillis) { + /** + * Sets the maximum backoff (in milliseconds) between retries. + * + * @param maxBackoffMillis The maximum backoff in milliseconds. + * @return The current Builder instance. + */ + public Builder maxBackoffMillis(final int maxBackoffMillis) { this.maxBackoffMillis = maxBackoffMillis; return this; } - public Builder jitterFactor(float jitterFactor) { + /** + * Sets the jitter factor (between 0 and 1) to control how much to jitter the backoff delay. + * + * @param jitterFactor The jitter factor. + * @return The current Builder instance. + */ + public Builder jitterFactor(final float jitterFactor) { this.jitterFactor = jitterFactor; return this; } + /** + * Builds and returns a new ExponentialBackoffStrategy instance. + * + * @return A new ExponentialBackoffStrategy. + */ public ExponentialBackoffStrategy build() { return new ExponentialBackoffStrategy( this.maxAttempts, this.backoffFactor, @@ -141,6 +160,11 @@ public ExponentialBackoffStrategy build() { } } + /** + * Creates a new Builder instance for ExponentialBackoffStrategy. + * + * @return A new Builder instance. + */ public static Builder builder() { return new Builder(); } diff --git a/src/main/java/com/fauna/client/Fauna.java b/src/main/java/com/fauna/client/Fauna.java index b9cd0fa8..2e8de5d4 100644 --- a/src/main/java/com/fauna/client/Fauna.java +++ b/src/main/java/com/fauna/client/Fauna.java @@ -2,7 +2,10 @@ import java.net.http.HttpClient; -public class Fauna { +public final class Fauna { + + private Fauna() { + } /** * Create a default Fauna client. @@ -19,7 +22,7 @@ public static FaunaClient client() { * @param config Fauna configuration object. * @return A FaunaClient (or subclass of it). */ - public static FaunaClient client(FaunaConfig config) { + public static FaunaClient client(final FaunaConfig config) { if (config == null) { throw new IllegalArgumentException("FaunaConfig cannot be null."); } @@ -34,8 +37,8 @@ public static FaunaClient client(FaunaConfig config) { * @param retryStrategy An implementation of RetryStrategy. * @return A FaunaClient (or subclass of it). */ - public static FaunaClient client(FaunaConfig config, HttpClient httpClient, - RetryStrategy retryStrategy) { + public static FaunaClient client(final FaunaConfig config, final HttpClient httpClient, + final RetryStrategy retryStrategy) { if (config == null) { throw new IllegalArgumentException("FaunaConfig cannot be null."); } @@ -49,7 +52,7 @@ public static FaunaClient client(FaunaConfig config, HttpClient httpClient, * @param database The name of the database. * @return A FaunaClient (or subclass of it). */ - public static FaunaClient scoped(FaunaClient client, String database) { + public static FaunaClient scoped(final FaunaClient client, final String database) { if (client == null) { throw new IllegalArgumentException("FaunaClient cannot be null."); } @@ -69,8 +72,8 @@ public static FaunaClient scoped(FaunaClient client, String database) { * @param role A Fauna role (either built-in or user defined). * @return A FaunaClient (or subclass of it). */ - public static FaunaClient scoped(FaunaClient client, String database, - FaunaRole role) { + public static FaunaClient scoped(final FaunaClient client, final String database, + final FaunaRole role) { if (client == null) { throw new IllegalArgumentException("FaunaClient cannot be null."); } diff --git a/src/main/java/com/fauna/client/FaunaClient.java b/src/main/java/com/fauna/client/FaunaClient.java index a6be4924..e29fb214 100644 --- a/src/main/java/com/fauna/client/FaunaClient.java +++ b/src/main/java/com/fauna/client/FaunaClient.java @@ -43,6 +43,10 @@ import static com.fauna.constants.ErrorMessages.QUERY_PAGE; import static com.fauna.constants.ErrorMessages.STREAM_SUBSCRIPTION; +/** + * A client to interact with the Fauna service, providing asynchronous and synchronous query execution, + * pagination, and streaming features. + */ public abstract class FaunaClient { public static final RetryStrategy DEFAULT_RETRY_STRATEGY = @@ -65,15 +69,29 @@ public abstract class FaunaClient { abstract RequestBuilder getFeedRequestBuilder(); - public FaunaClient(String secret, Logger logger, - StatsCollector statsCollector) { + /** + * Constructs a FaunaClient with the provided secret and logger. + * + * @param secret The Fauna secret used for authentication. + * @param logger The logger instance. + * @param statsCollector A collector for tracking statistics. + */ + public FaunaClient(final String secret, final Logger logger, + final StatsCollector statsCollector) { this.faunaSecret = secret; this.logger = logger; this.statsCollector = statsCollector; } - public FaunaClient(String secret, Handler logHandler, - StatsCollector statsCollector) { + /** + * Constructs a FaunaClient with the provided secret and log handler. + * + * @param secret The Fauna secret used for authentication. + * @param logHandler The handler to manage log outputs. + * @param statsCollector A collector for tracking statistics. + */ + public FaunaClient(final String secret, final Handler logHandler, + final StatsCollector statsCollector) { this.faunaSecret = secret; this.logger = Logger.getLogger(this.getClass().getCanonicalName()); this.logger.addHandler(logHandler); @@ -81,25 +99,45 @@ public FaunaClient(String secret, Handler logHandler, this.statsCollector = statsCollector; } + /** + * Retrieves the Fauna secret used for authentication. + * + * @return The Fauna secret. + */ protected String getFaunaSecret() { return this.faunaSecret; } + /** + * Retrieves the logger used for logging Fauna client activity. + * + * @return The logger instance. + */ public Logger getLogger() { return this.logger; } + /** + * Retrieves the stats collector instance. + * + * @return The stats collector instance. + */ public StatsCollector getStatsCollector() { return this.statsCollector; } + /** + * Retrieves the last known transaction timestamp. + * + * @return An Optional containing the last transaction timestamp, if available. + */ public Optional getLastTransactionTs() { long ts = lastTransactionTs.get(); return ts > 0 ? Optional.of(ts) : Optional.empty(); } private static Optional extractServiceException( - Throwable throwable) { + final Throwable throwable) { if (throwable instanceof ServiceException) { return Optional.of((ServiceException) throwable); } else if (throwable.getCause() instanceof ServiceException) { @@ -109,7 +147,7 @@ private static Optional extractServiceException( } } - private void updateTs(QueryResponse resp) { + private void updateTs(final QueryResponse resp) { Long newTs = resp.getLastSeenTxn(); if (newTs != null) { this.lastTransactionTs.updateAndGet( @@ -117,8 +155,8 @@ private void updateTs(QueryResponse resp) { } } - private void completeRequest(QuerySuccess success, - Throwable throwable) { + private void completeRequest(final QuerySuccess success, + final Throwable throwable) { if (success != null) { updateTs(success); } else if (throwable != null) { @@ -127,8 +165,8 @@ private void completeRequest(QuerySuccess success, } } - private void completeFeedRequest(FeedPage success, - Throwable throwable) { + private void completeFeedRequest(final FeedPage success, + final Throwable throwable) { // Feeds do not update the clients latest transaction timestamp. if (throwable != null) { extractServiceException(throwable).ifPresent( @@ -136,16 +174,15 @@ private void completeFeedRequest(FeedPage success, } } - private void logResponse(HttpResponse response) { + private void logResponse(final HttpResponse response) { logger.fine(MessageFormat.format( "Fauna HTTP Response {0} from {1}, headers: {2}", response.statusCode(), response.uri(), headersAsString(response.headers()))); - // We could implement a LoggingInputStream or something to log the response here. } private Supplier>> makeAsyncRequest( - HttpClient client, HttpRequest request, Codec codec) { + final HttpClient client, final HttpRequest request, final Codec codec) { return () -> client.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()).thenApply( response -> { @@ -156,7 +193,7 @@ private Supplier>> makeAsyncRequest( } private Supplier>> makeAsyncFeedRequest( - HttpClient client, HttpRequest request, Codec codec) { + final HttpClient client, final HttpRequest request, final Codec codec) { return () -> client.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()).thenApply( response -> { @@ -166,13 +203,11 @@ private Supplier>> makeAsyncFeedRequest( }).whenComplete(this::completeFeedRequest); } - private R completeAsync(CompletableFuture future, - String executionMessage) { + private R completeAsync(final CompletableFuture future, final String executionMessage) { try { return future.get(); } catch (ExecutionException | InterruptedException exc) { - if (exc.getCause() != null && - exc.getCause() instanceof FaunaException) { + if (exc.getCause() != null && exc.getCause() instanceof FaunaException) { throw (FaunaException) exc.getCause(); } else { logger.warning( @@ -196,7 +231,7 @@ private R completeAsync(CompletableFuture future, * @return QuerySuccess The successful query result. * @throws FaunaException If the query does not succeed, an exception will be thrown. */ - public CompletableFuture> asyncQuery(Query fql) { + public CompletableFuture> asyncQuery(final Query fql) { if (Objects.isNull(fql)) { throw new IllegalArgumentException( "The provided FQL query is null."); @@ -221,10 +256,12 @@ public CompletableFuture> asyncQuery(Query fql) { * @param options A (nullable) set of options to pass to the query. * @return QuerySuccess The successful query result. * @throws FaunaException If the query does not succeed, an exception will be thrown. + * + * @param The return type of the query. */ - public CompletableFuture> asyncQuery(Query fql, - Class resultClass, - QueryOptions options) { + public CompletableFuture> asyncQuery(final Query fql, + final Class resultClass, + final QueryOptions options) { if (Objects.isNull(fql)) { throw new IllegalArgumentException( "The provided FQL query is null."); @@ -249,10 +286,12 @@ public CompletableFuture> asyncQuery(Query fql, * @param options A (nullable) set of options to pass to the query. * @return QuerySuccess The successful query result. * @throws FaunaException If the query does not succeed, an exception will be thrown. + * + * @param The inner type for the parameterized wrapper. */ - public CompletableFuture> asyncQuery(Query fql, - ParameterizedOf parameterizedType, - QueryOptions options) { + public CompletableFuture> asyncQuery(final Query fql, + final ParameterizedOf parameterizedType, + final QueryOptions options) { if (Objects.isNull(fql)) { throw new IllegalArgumentException( "The provided FQL query is null."); @@ -279,9 +318,11 @@ public CompletableFuture> asyncQuery(Query fql, * @param resultClass The expected class of the query result. * @return QuerySuccess A CompletableFuture that completes with the successful query result. * @throws FaunaException If the query does not succeed, an exception will be thrown. + * + * @param The return type of the query. */ - public CompletableFuture> asyncQuery(Query fql, - Class resultClass) { + public CompletableFuture> asyncQuery(final Query fql, + final Class resultClass) { return asyncQuery(fql, resultClass, null); } @@ -296,9 +337,10 @@ public CompletableFuture> asyncQuery(Query fql, * @param parameterizedType The expected class of the query result. * @return QuerySuccess The successful query result. * @throws FaunaException If the query does not succeed, an exception will be thrown. + * @param The inner type for the parameterized wrapper. */ - public CompletableFuture> asyncQuery(Query fql, - ParameterizedOf parameterizedType) { + public CompletableFuture> asyncQuery(final Query fql, + final ParameterizedOf parameterizedType) { if (Objects.isNull(fql)) { throw new IllegalArgumentException( "The provided FQL query is null."); @@ -327,7 +369,7 @@ public CompletableFuture> asyncQuery(Query fql, * @return QuerySuccess The successful query result. * @throws FaunaException If the query does not succeed, an exception will be thrown. */ - public QuerySuccess query(Query fql) throws FaunaException { + public QuerySuccess query(final Query fql) throws FaunaException { return completeAsync(asyncQuery(fql, Object.class, null), "Unable to execute query."); } @@ -342,8 +384,9 @@ public QuerySuccess query(Query fql) throws FaunaException { * @param resultClass The expected class of the query result. * @return QuerySuccess The successful query result. * @throws FaunaException If the query does not succeed, an exception will be thrown. + * @param The return type of the query. */ - public QuerySuccess query(Query fql, Class resultClass) + public QuerySuccess query(final Query fql, final Class resultClass) throws FaunaException { return completeAsync(asyncQuery(fql, resultClass, null), QUERY_EXECUTION); @@ -359,9 +402,10 @@ public QuerySuccess query(Query fql, Class resultClass) * @param parameterizedType The expected class of the query result. * @return QuerySuccess The successful query result. * @throws FaunaException If the query does not succeed, an exception will be thrown. + * @param The inner type for the parameterized wrapper. */ - public QuerySuccess query(Query fql, - ParameterizedOf parameterizedType) + public QuerySuccess query(final Query fql, + final ParameterizedOf parameterizedType) throws FaunaException { return completeAsync(asyncQuery(fql, parameterizedType), QUERY_EXECUTION); @@ -378,9 +422,10 @@ public QuerySuccess query(Query fql, * @param options A (nullable) set of options to pass to the query. * @return QuerySuccess The successful query result. * @throws FaunaException If the query does not succeed, an exception will be thrown. + * @param The return type of the query. */ - public QuerySuccess query(Query fql, Class resultClass, - QueryOptions options) + public QuerySuccess query(final Query fql, final Class resultClass, + final QueryOptions options) throws FaunaException { return completeAsync(asyncQuery(fql, resultClass, options), QUERY_EXECUTION); @@ -397,10 +442,11 @@ public QuerySuccess query(Query fql, Class resultClass, * @param options A (nullable) set of options to pass to the query. * @return QuerySuccess The successful query result. * @throws FaunaException If the query does not succeed, an exception will be thrown. + * @param The inner type for the parameterized wrapper. */ - public QuerySuccess query(Query fql, - ParameterizedOf parameterizedType, - QueryOptions options) + public QuerySuccess query(final Query fql, + final ParameterizedOf parameterizedType, + final QueryOptions options) throws FaunaException { return completeAsync(asyncQuery(fql, parameterizedType, options), QUERY_EXECUTION); @@ -420,7 +466,7 @@ public QuerySuccess query(Query fql, * @throws FaunaException If the query does not succeed, an exception will be thrown. */ public CompletableFuture>> asyncQueryPage( - AfterToken after, Class elementClass, QueryOptions options) { + final AfterToken after, final Class elementClass, final QueryOptions options) { return this.asyncQuery(PageIterator.buildPageQuery(after), pageOf(elementClass), options); } @@ -436,7 +482,7 @@ public CompletableFuture>> asyncQueryPage( * @throws FaunaException If the query does not succeed, an exception will be thrown. */ public QuerySuccess> queryPage( - AfterToken after, Class elementClass, QueryOptions options) { + final AfterToken after, final Class elementClass, final QueryOptions options) { return completeAsync(asyncQueryPage(after, elementClass, options), QUERY_PAGE); } @@ -454,8 +500,8 @@ public QuerySuccess> queryPage( * @return QuerySuccess The successful query result. * @throws FaunaException If the query does not succeed, an exception will be thrown. */ - public PageIterator paginate(Query fql, Class elementClass, - QueryOptions options) { + public PageIterator paginate(final Query fql, final Class elementClass, + final QueryOptions options) { return new PageIterator<>(this, fql, elementClass, options); } @@ -466,7 +512,7 @@ public PageIterator paginate(Query fql, Class elementClass, * @return The successful query result. * @throws FaunaException If the query does not succeed, an exception will be thrown. */ - public PageIterator paginate(Query fql) { + public PageIterator paginate(final Query fql) { return paginate(fql, Object.class, null); } @@ -478,7 +524,7 @@ public PageIterator paginate(Query fql) { * @return The successful query result. * @throws FaunaException If the query does not succeed, an exception will be thrown. */ - public PageIterator paginate(Query fql, QueryOptions options) { + public PageIterator paginate(final Query fql, final QueryOptions options) { return paginate(fql, Object.class, options); } @@ -489,8 +535,9 @@ public PageIterator paginate(Query fql, QueryOptions options) { * @param elementClass The expected class of the query result. * @return QuerySuccess The successful query result. * @throws FaunaException If the query does not succeed, an exception will be thrown. + * @param The type for each element in a page. */ - public PageIterator paginate(Query fql, Class elementClass) { + public PageIterator paginate(final Query fql, final Class elementClass) { return paginate(fql, elementClass, null); } //endregion @@ -504,12 +551,15 @@ public PageIterator paginate(Query fql, Class elementClass) { * * @param eventSource The Event Source (e.g. token from `.eventSource()`). * @param streamOptions The Stream Options (including start timestamp, retry strategy). + * @param elementClass The target type into which event data will be deserialized. * @return CompletableFuture A CompletableFuture of FaunaStream. * @throws FaunaException If the query does not succeed, an exception will be thrown. + * @param The type for data in an event. */ public CompletableFuture> asyncStream( - EventSource eventSource, - StreamOptions streamOptions, Class elementClass) { + final EventSource eventSource, + final StreamOptions streamOptions, + final Class elementClass) { HttpRequest streamReq = getStreamRequestBuilder().buildStreamRequest(eventSource, streamOptions); @@ -530,14 +580,16 @@ public CompletableFuture> asyncStream( * Send a request to the Fauna stream endpoint to start a stream, and return a FaunaStream publisher. * * @param eventSource The request object including a stream token, and optionally a cursor, or timestamp. - * @param streamOptions + * @param streamOptions The stream options. * @param elementClass The expected class <E> of the stream events. - * @return FaunaStream A publisher, implementing Flow.Publisher<StreamEvent<E>> from the Java Flow API. + * @return FaunaStream A publisher, implementing Flow.Publisher<StreamEvent<E>> from the Java Flow + * API. * @throws FaunaException If the query does not succeed, an exception will be thrown. + * @param The type for data in an event. */ - public FaunaStream stream(EventSource eventSource, - StreamOptions streamOptions, - Class elementClass) { + public FaunaStream stream(final EventSource eventSource, + final StreamOptions streamOptions, + final Class elementClass) { return completeAsync( asyncStream(eventSource, streamOptions, elementClass), STREAM_SUBSCRIPTION); @@ -553,11 +605,13 @@ public FaunaStream stream(EventSource eventSource, * * @param fql The FQL query to be executed. It must return an event source, e.g. ends in `.eventSource()`. * @param elementClass The expected class <E> of the stream events. - * @return FaunaStream A publisher, implementing Flow.Publisher<StreamEvent<E>> from the Java Flow API. + * @return FaunaStream A publisher, implementing Flow.Publisher<StreamEvent<E>> from the Java Flow + * API. * @throws FaunaException If the query does not succeed, an exception will be thrown. + * @param The type for data in an event. */ - public CompletableFuture> asyncStream(Query fql, - Class elementClass) { + public CompletableFuture> asyncStream(final Query fql, + final Class elementClass) { return this.asyncQuery(fql, EventSourceResponse.class) .thenApply(queryResponse -> this.stream(EventSource.fromResponse( @@ -577,10 +631,12 @@ public CompletableFuture> asyncStream(Query fql, * * @param fql The FQL query to be executed. It must return a stream, e.g. ends in `.toStream()`. * @param elementClass The expected class <E> of the stream events. - * @return FaunaStream A publisher, implementing Flow.Publisher<StreamEvent<E>> from the Java Flow API. + * @return FaunaStream A publisher, implementing Flow.Publisher<StreamEvent<E>> from the Java Flow + * API. * @throws FaunaException If the query does not succeed, an exception will be thrown. + * @param The type for data in an event. */ - public FaunaStream stream(Query fql, Class elementClass) { + public FaunaStream stream(final Query fql, final Class elementClass) { return completeAsync(asyncStream(fql, elementClass), STREAM_SUBSCRIPTION); } @@ -594,18 +650,17 @@ public FaunaStream stream(Query fql, Class elementClass) { * @param eventSource An EventSource object (e.g. token from `.eventSource()`) * @param feedOptions The FeedOptions object (default options will be used if null). * @param elementClass The expected class <E> of the feed events. - * @param The type of the feed events. + * @param The type for data in an event. * @return CompletableFuture A CompletableFuture that completes with a FeedPage<E>. */ - public CompletableFuture> poll(EventSource eventSource, - FeedOptions feedOptions, - Class elementClass) { + public CompletableFuture> poll(final EventSource eventSource, + final FeedOptions feedOptions, + final Class elementClass) { return new RetryHandler>(getRetryStrategy(), logger).execute(makeAsyncFeedRequest( getHttpClient(), getFeedRequestBuilder().buildFeedRequest(eventSource, - feedOptions != null ? feedOptions : - FeedOptions.DEFAULT), + feedOptions != null ? feedOptions : FeedOptions.DEFAULT), codecProvider.get(elementClass))); } @@ -617,12 +672,12 @@ public CompletableFuture> poll(EventSource eventSource, * @param fql The FQL query to be executed. It must return a token, e.g. ends in `.changesOn()`. * @param feedOptions The FeedOptions object (must not be null). * @param elementClass The expected class <E> of the feed events. - * @param The type of the feed events. + * @param The type for data in an event. * @return FeedIterator A CompletableFuture that completes with a feed iterator that returns pages of Feed events. */ - public CompletableFuture> asyncFeed(Query fql, - FeedOptions feedOptions, - Class elementClass) { + public CompletableFuture> asyncFeed(final Query fql, + final FeedOptions feedOptions, + final Class elementClass) { return this.asyncQuery(fql, EventSourceResponse.class).thenApply( success -> this.feed( EventSource.fromResponse(success.getData()), @@ -636,11 +691,10 @@ public CompletableFuture> asyncFeed(Query fql, * @param fql The FQL query to be executed. It must return a token, e.g. ends in `.changesOn()`. * @param feedOptions The Feed Op * @param elementClass The expected class <E> of the feed events. - * @param The type of the feed events. + * @param The type for data in an event. * @return FeedIterator An iterator that returns pages of Feed events. */ - public FeedIterator feed(Query fql, FeedOptions feedOptions, - Class elementClass) { + public FeedIterator feed(final Query fql, final FeedOptions feedOptions, final Class elementClass) { return completeAsync(asyncFeed(fql, feedOptions, elementClass), FEED_SUBSCRIPTION); } @@ -649,13 +703,14 @@ public FeedIterator feed(Query fql, FeedOptions feedOptions, * Send a request to the Feed endpoint and return a FeedIterator. * * @param eventSource The Fauna Event Source. + * @param feedOptions The feed options. * @param elementClass The expected class <E> of the feed events. - * @param The type of the feed events. + * @param The type for data in an event. * @return FeedIterator An iterator that returns pages of Feed events. */ - public FeedIterator feed(EventSource eventSource, - FeedOptions feedOptions, - Class elementClass) { + public FeedIterator feed(final EventSource eventSource, + final FeedOptions feedOptions, + final Class elementClass) { return new FeedIterator<>(this, eventSource, feedOptions, elementClass); } diff --git a/src/main/java/com/fauna/client/FaunaConfig.java b/src/main/java/com/fauna/client/FaunaConfig.java index 4fde3d59..28b15a4a 100644 --- a/src/main/java/com/fauna/client/FaunaConfig.java +++ b/src/main/java/com/fauna/client/FaunaConfig.java @@ -14,7 +14,7 @@ * FaunaConfig is a configuration class used to set up and configure a connection to Fauna. * It encapsulates various settings such as the endpoint URL, secret key, and more. */ -public class FaunaConfig { +public final class FaunaConfig { public static class FaunaEndpoint { public static final String DEFAULT = "https://db.fauna.com"; @@ -36,9 +36,8 @@ public static class FaunaEndpoint { * * @param builder The builder used to create the FaunaConfig instance. */ - private FaunaConfig(Builder builder) { - this.endpoint = builder.endpoint != null ? builder.endpoint : - FaunaEndpoint.DEFAULT; + private FaunaConfig(final Builder builder) { + this.endpoint = builder.endpoint != null ? builder.endpoint : FaunaEndpoint.DEFAULT; this.secret = builder.secret != null ? builder.secret : ""; this.maxContentionRetries = builder.maxContentionRetries; this.clientTimeoutBuffer = builder.clientTimeoutBuffer; @@ -122,7 +121,7 @@ public static class Builder { private Handler logHandler = defaultLogHandler(); private StatsCollector statsCollector = new StatsCollectorImpl(); - static Level getLogLevel(String debug) { + static Level getLogLevel(final String debug) { if (debug == null || debug.isBlank()) { return Level.WARNING; } else { @@ -148,7 +147,7 @@ private static Handler defaultLogHandler() { * @param endpoint A String representing the endpoint URL. * @return The current Builder instance. */ - public Builder endpoint(String endpoint) { + public Builder endpoint(final String endpoint) { this.endpoint = endpoint; return this; } @@ -159,7 +158,7 @@ public Builder endpoint(String endpoint) { * @param secret A String representing the secret key. * @return The current Builder instance. */ - public Builder secret(String secret) { + public Builder secret(final String secret) { this.secret = secret; return this; } @@ -170,15 +169,18 @@ public Builder secret(String secret) { * @param maxContentionRetries A positive integer value. * @return The current Builder instance. */ - public Builder maxContentionRetries(int maxContentionRetries) { + public Builder maxContentionRetries(final int maxContentionRetries) { this.maxContentionRetries = maxContentionRetries; return this; } /** * Set the client timeout buffer. + * + * @param duration The timeout buffer duration. + * @return The current Builder instance. */ - public Builder clientTimeoutBuffer(Duration duration) { + public Builder clientTimeoutBuffer(final Duration duration) { this.clientTimeoutBuffer = duration; return this; } @@ -189,7 +191,7 @@ public Builder clientTimeoutBuffer(Duration duration) { * @param handler A log handler instance. * @return The current Builder instance. */ - public Builder logHandler(Handler handler) { + public Builder logHandler(final Handler handler) { this.logHandler = handler; return this; } @@ -200,7 +202,7 @@ public Builder logHandler(Handler handler) { * @param statsCollector A stats collector instance. * @return The current Builder instance. */ - public Builder statsCollector(StatsCollector statsCollector) { + public Builder statsCollector(final StatsCollector statsCollector) { this.statsCollector = statsCollector; return this; } @@ -223,10 +225,9 @@ public static class FaunaEnvironment { private static final String FAUNA_ENDPOINT = "FAUNA_ENDPOINT"; private static final String FAUNA_DEBUG = "FAUNA_DEBUG"; - private static Optional environmentVariable(String name) { + private static Optional environmentVariable(final String name) { Optional var = Optional.ofNullable(System.getenv(name)); - return var.isPresent() && var.get().isBlank() ? Optional.empty() : - var; + return var.isPresent() && var.get().isBlank() ? Optional.empty() : var; } /** diff --git a/src/main/java/com/fauna/client/FaunaRole.java b/src/main/java/com/fauna/client/FaunaRole.java index 6f531adb..fc17bb56 100644 --- a/src/main/java/com/fauna/client/FaunaRole.java +++ b/src/main/java/com/fauna/client/FaunaRole.java @@ -7,7 +7,7 @@ * Built-in roles defined at: * docs.fauna.com. */ -public class FaunaRole { +public final class FaunaRole { private static final String ADMIN_ROLE_NAME = "admin"; private static final String SERVER_ROLE_NAME = "server"; private static final String SERVER_READ_ONLY_ROLE_NAME = "server-readonly"; @@ -29,15 +29,23 @@ public class FaunaRole { * * @param role The role name, either @role/name or one of the built-in role names. */ - FaunaRole(String role) { + FaunaRole(final String role) { this.role = role; } + /** + * @return A {@link String} representation of the {@code FaunaRole}. + */ public String toString() { return this.role; } - public static void validateRoleName(String name) { + /** + * Validates a role name. + * + * @param name The name of the role to validate. + */ + public static void validateRoleName(final String name) { if (name == null || name.isEmpty()) { throw new IllegalArgumentException( "Role name cannot be null or empty."); @@ -62,7 +70,13 @@ public static void validateRoleName(String name) { } - public static FaunaRole named(String name) { + /** + * Creates a {@code FaunaRole} with the desired name prepended with {@code @role/}. + * + * @param name The name of the role to use. + * @return A {@code FaunaRole} instance. + */ + public static FaunaRole named(final String name) { validateRoleName(name); return new FaunaRole(ROLE_PREFIX + name); } diff --git a/src/main/java/com/fauna/client/FaunaScope.java b/src/main/java/com/fauna/client/FaunaScope.java index 43795c21..bc332a6d 100644 --- a/src/main/java/com/fauna/client/FaunaScope.java +++ b/src/main/java/com/fauna/client/FaunaScope.java @@ -1,42 +1,80 @@ package com.fauna.client; -public class FaunaScope { +/** + * Represents a FaunaScope, a structure that encapsulates a Fauna database and a role within that database. + * The FaunaScope is used to generate a token that is used for authorization. + */ +public final class FaunaScope { private static final String DELIMITER = ":"; private final String database; private final FaunaRole role; - public FaunaScope(String database, FaunaRole role) { + /** + * Creates a FaunaScope with the specified database and role. + * + * @param database the name of the database + * @param role the FaunaRole associated with this scope + */ + public FaunaScope(final String database, final FaunaRole role) { this.database = database; this.role = role; } - public String getToken(String secret) { + /** + * Generates a token for this scope using the provided secret. + * + * @param secret the secret used to generate the token + * @return a token string formed by concatenating secret, database, and role + */ + public String getToken(final String secret) { return String.join(DELIMITER, secret, database, role.toString()); } + /** + * A builder class for creating instances of FaunaScope. + */ public static class Builder { - public final String database; - public FaunaRole role = null; + private final String database; + private FaunaRole role = null; - public Builder(String database) { + /** + * Constructs a Builder for FaunaScope. + * + * @param database the name of the database + */ + public Builder(final String database) { this.database = database; } - public Builder withRole(FaunaRole role) { + /** + * Sets the role for the FaunaScope. + * + * @param role the FaunaRole to associate with the scope + * @return the Builder instance for method chaining + */ + public Builder withRole(final FaunaRole role) { this.role = role; return this; } + /** + * Builds a FaunaScope instance using the current builder settings. + * + * @return a newly created FaunaScope + */ public FaunaScope build() { return new FaunaScope(this.database, this.role != null ? this.role : FaunaRole.SERVER); - } } - public static Builder builder(String database) { + /** + * Creates a new Builder instance for a FaunaScope. + * + * @param database the name of the database + * @return a new Builder instance + */ + public static Builder builder(final String database) { return new Builder(database); } - - } diff --git a/src/main/java/com/fauna/client/Logging.java b/src/main/java/com/fauna/client/Logging.java index bd5bd945..9db130a3 100644 --- a/src/main/java/com/fauna/client/Logging.java +++ b/src/main/java/com/fauna/client/Logging.java @@ -3,8 +3,21 @@ import java.net.http.HttpHeaders; import java.util.stream.Collectors; -public class Logging { - public static String headersAsString(HttpHeaders headers) { +/** + * A utility class for logging HTTP headers. + */ +public final class Logging { + + private Logging() { + } + + /** + * Converts the given HttpHeaders to a string representation. + * + * @param headers The HttpHeaders to convert. + * @return A string representation of the headers. + */ + public static String headersAsString(final HttpHeaders headers) { String hdrs = "NONE"; if (headers != null) { hdrs = headers.map().entrySet().stream().map( diff --git a/src/main/java/com/fauna/client/NoRetryStrategy.java b/src/main/java/com/fauna/client/NoRetryStrategy.java index e32ee621..34a19a90 100644 --- a/src/main/java/com/fauna/client/NoRetryStrategy.java +++ b/src/main/java/com/fauna/client/NoRetryStrategy.java @@ -1,15 +1,17 @@ package com.fauna.client; -public class NoRetryStrategy implements RetryStrategy { - +/** + * Specifies that no retries will be made. + */ +public final class NoRetryStrategy implements RetryStrategy { @Override - public boolean canRetry(int retryAttempt) { + public boolean canRetry(final int retryAttempt) { return false; } @Override - public int getDelayMillis(int retryAttempt) { + public int getDelayMillis(final int retryAttempt) { return 0; } diff --git a/src/main/java/com/fauna/client/PageIterator.java b/src/main/java/com/fauna/client/PageIterator.java index dfd1fe8f..6cb8bf94 100644 --- a/src/main/java/com/fauna/client/PageIterator.java +++ b/src/main/java/com/fauna/client/PageIterator.java @@ -1,6 +1,5 @@ package com.fauna.client; - import com.fauna.codec.PageOf; import com.fauna.exception.FaunaException; import com.fauna.query.AfterToken; @@ -20,7 +19,7 @@ /** * PageIterator iterates over paged responses from Fauna, the default page size is 16. * - * @param + * @param The type of elements in the page. */ public class PageIterator implements Iterator> { static final String TOKEN_NAME = "token"; @@ -35,11 +34,11 @@ public class PageIterator implements Iterator> { * * @param client A client that makes requests to Fauna. * @param fql The FQL query. - * @param resultClass The class of the elements returned from Fauna (i.e. the rows). + * @param resultClass The class of the elements returned from Fauna (i.e., the rows). * @param options (optionally) pass in QueryOptions. */ - public PageIterator(FaunaClient client, Query fql, Class resultClass, - QueryOptions options) { + public PageIterator(final FaunaClient client, final Query fql, final Class resultClass, + final QueryOptions options) { this.client = client; this.pageClass = new PageOf<>(resultClass); this.options = options; @@ -47,8 +46,16 @@ public PageIterator(FaunaClient client, Query fql, Class resultClass, this.queryFuture = client.asyncQuery(fql, this.pageClass, options); } - public PageIterator(FaunaClient client, Page firstPage, - Class resultClass, QueryOptions options) { + /** + * Construct a new PageIterator starting from a given page. + * + * @param client A client that makes requests to Fauna. + * @param firstPage The first Page of elements. + * @param resultClass The class of the elements returned from Fauna (i.e., the rows). + * @param options (optionally) pass in QueryOptions. + */ + public PageIterator(final FaunaClient client, final Page firstPage, + final Class resultClass, final QueryOptions options) { this.client = client; this.pageClass = new PageOf<>(resultClass); this.options = options; @@ -56,21 +63,40 @@ public PageIterator(FaunaClient client, Page firstPage, .ifPresentOrElse(this::doPaginatedQuery, this::endPagination); } + /** + * Check if there is a next page available. + * + * @return True if there is a next page, false otherwise. + */ @Override public boolean hasNext() { return this.queryFuture != null; } - public static Query buildPageQuery(AfterToken afterToken) { + /** + * Build the page query with a specific AfterToken. + * + * @param afterToken The token indicating where the next page should start. + * @return A Query to fetch the next page. + */ + public static Query buildPageQuery(final AfterToken afterToken) { return fql(PAGINATE_QUERY, Map.of(TOKEN_NAME, afterToken.getToken())); } - private void doPaginatedQuery(AfterToken afterToken) { + /** + * Performs a paginated query with the provided AfterToken. + * + * @param afterToken The token indicating where the next page should start. + */ + private void doPaginatedQuery(final AfterToken afterToken) { this.queryFuture = client.asyncQuery(PageIterator.buildPageQuery(afterToken), pageClass, options); } + /** + * Ends the pagination process when no further pages are available. + */ private void endPagination() { this.queryFuture = null; } @@ -78,7 +104,7 @@ private void endPagination() { /** * Returns a CompletableFuture that will complete with the next page (or throw a FaunaException). * - * @return A c + * @return A CompletableFuture representing the next page of elements. */ public CompletableFuture> nextAsync() { if (this.queryFuture != null) { @@ -93,7 +119,6 @@ public CompletableFuture> nextAsync() { } } - /** * Get the next Page. * @@ -105,8 +130,7 @@ public Page next() { try { return nextAsync().join(); } catch (CompletionException ce) { - if (ce.getCause() != null && - ce.getCause() instanceof FaunaException) { + if (ce.getCause() != null && ce.getCause() instanceof FaunaException) { throw (FaunaException) ce.getCause(); } else { throw ce; @@ -122,15 +146,26 @@ public Page next() { public Iterator flatten() { return new Iterator<>() { private final PageIterator pageIterator = PageIterator.this; - private Iterator thisPage = pageIterator.hasNext() ? - pageIterator.next().getData().iterator() : null; - + private Iterator thisPage = pageIterator.hasNext() + ? pageIterator.next().getData().iterator() + : null; + + /** + * Check if there are more items to iterate over. + * + * @return True if there are more items, false otherwise. + */ @Override public boolean hasNext() { - return thisPage != null && - (thisPage.hasNext() || pageIterator.hasNext()); + return thisPage != null && (thisPage.hasNext() || pageIterator.hasNext()); } + /** + * Get the next item in the iteration. + * + * @return The next item in the iteration. + * @throws NoSuchElementException if no more items are available. + */ @Override public E next() { if (thisPage == null) { diff --git a/src/main/java/com/fauna/client/RequestBuilder.java b/src/main/java/com/fauna/client/RequestBuilder.java index e75abf00..fd4ac0b7 100644 --- a/src/main/java/com/fauna/client/RequestBuilder.java +++ b/src/main/java/com/fauna/client/RequestBuilder.java @@ -25,7 +25,7 @@ /** * The RequestBuilder class is responsible for building HTTP requests for communicating with Fauna. */ -public class RequestBuilder { +public final class RequestBuilder { private static final String BEARER = "Bearer"; private static final String QUERY_PATH = "/query/1"; @@ -36,6 +36,9 @@ public class RequestBuilder { private final Duration clientTimeoutBuffer; private final Logger logger; + /** + * Field names for HTTP requests. + */ public static class FieldNames { static final String QUERY = "query"; public static final String TOKEN = "token"; @@ -44,6 +47,9 @@ public static class FieldNames { public static final String PAGE_SIZE = "page_size"; } + /** + * HTTP headers used for Fauna requests. + */ static class Headers { static final String LAST_TXN_TS = "X-Last-Txn-Ts"; static final String LINEARIZED = "X-Linearized"; @@ -60,9 +66,17 @@ static class Headers { static final String FORMAT = "X-Format"; } - public RequestBuilder(URI uri, String token, int maxContentionRetries, - Duration clientTimeoutBuffer, Logger logger) { - // DriverEnvironment is not needed outside the constructor for now. + /** + * Constructor for creating a RequestBuilder with the specified Fauna configuration. + * + * @param uri The URI for the Fauna endpoint. + * @param token The secret key used for authorization. + * @param maxContentionRetries The maximum retries for contention errors. + * @param clientTimeoutBuffer The buffer for the client timeout. + * @param logger The logger to log HTTP request details. + */ + public RequestBuilder(final URI uri, final String token, final int maxContentionRetries, + final Duration clientTimeoutBuffer, final Logger logger) { DriverEnvironment env = new DriverEnvironment(DriverEnvironment.JvmDriver.JAVA); this.baseRequestBuilder = HttpRequest.newBuilder().uri(uri).headers( @@ -80,43 +94,76 @@ Headers.AUTHORIZATION, buildAuthHeader(token) this.logger = logger; } - public RequestBuilder(HttpRequest.Builder builder, - Duration clientTimeoutBuffer, Logger logger) { + /** + * Constructor for creating a RequestBuilder with an existing HttpRequest.Builder. + * + * @param builder The HttpRequest.Builder to use. + * @param clientTimeoutBuffer The buffer for the client timeout. + * @param logger The logger to log HTTP request details. + */ + public RequestBuilder(final HttpRequest.Builder builder, + final Duration clientTimeoutBuffer, final Logger logger) { this.baseRequestBuilder = builder; this.clientTimeoutBuffer = clientTimeoutBuffer; this.logger = logger; } - public static RequestBuilder queryRequestBuilder(FaunaConfig config, - Logger logger) { + /** + * Creates a new RequestBuilder for Fauna queries. + * + * @param config The FaunaConfig containing endpoint and secret. + * @param logger The logger for logging HTTP request details. + * @return A new instance of RequestBuilder. + */ + public static RequestBuilder queryRequestBuilder(final FaunaConfig config, + final Logger logger) { return new RequestBuilder(URI.create(config.getEndpoint() + QUERY_PATH), config.getSecret(), config.getMaxContentionRetries(), config.getClientTimeoutBuffer(), logger); } - public static RequestBuilder streamRequestBuilder(FaunaConfig config, - Logger logger) { + /** + * Creates a new RequestBuilder for Fauna streams. + * + * @param config The FaunaConfig containing endpoint and secret. + * @param logger The logger for logging HTTP request details. + * @return A new instance of RequestBuilder. + */ + public static RequestBuilder streamRequestBuilder(final FaunaConfig config, + final Logger logger) { return new RequestBuilder( URI.create(config.getEndpoint() + STREAM_PATH), config.getSecret(), config.getMaxContentionRetries(), config.getClientTimeoutBuffer(), logger); } - public static RequestBuilder feedRequestBuilder(FaunaConfig config, - Logger logger) { + /** + * Creates a new RequestBuilder for Fauna feed requests. + * + * @param config The FaunaConfig containing endpoint and secret. + * @param logger The logger for logging HTTP request details. + * @return A new instance of RequestBuilder. + */ + public static RequestBuilder feedRequestBuilder(final FaunaConfig config, + final Logger logger) { return new RequestBuilder(URI.create(config.getEndpoint() + FEED_PATH), config.getSecret(), config.getMaxContentionRetries(), config.getClientTimeoutBuffer(), logger); } - public RequestBuilder scopedRequestBuilder(String token) { + /** + * Creates a scoped request builder with the given token. + * + * @param token The token to be used for the request's authorization header. + * @return A new instance of RequestBuilder with the scoped token. + */ + public RequestBuilder scopedRequestBuilder(final String token) { HttpRequest.Builder newBuilder = this.baseRequestBuilder.copy(); - // .setHeader(..) clears existing headers (which we want) while .header(..) would append it :) newBuilder.setHeader(Headers.AUTHORIZATION, buildAuthHeader(token)); return new RequestBuilder(newBuilder, clientTimeoutBuffer, logger); } - private void logRequest(String body, HttpRequest req) { + private void logRequest(final String body, final HttpRequest req) { String timeout = req.timeout().map( val -> MessageFormat.format(" (timeout: {0})", val)).orElse(""); logger.fine(MessageFormat.format( @@ -129,12 +176,15 @@ private void logRequest(String body, HttpRequest req) { /** * Builds and returns an HTTP request for a given Fauna query string (FQL). * - * @param fql The Fauna query string. + * @param fql The Fauna query string. + * @param options The query options. + * @param provider The codec provider to encode the query. + * @param lastTxnTs The last transaction timestamp (optional). * @return An HttpRequest object configured for the Fauna query. */ - public HttpRequest buildRequest(Query fql, QueryOptions options, - CodecProvider provider, Long last_txn_ts) { - HttpRequest.Builder builder = getBuilder(options, last_txn_ts); + public HttpRequest buildRequest(final Query fql, final QueryOptions options, + final CodecProvider provider, final Long lastTxnTs) { + HttpRequest.Builder builder = getBuilder(options, lastTxnTs); try (UTF8FaunaGenerator gen = UTF8FaunaGenerator.create()) { gen.writeStartObject(); gen.writeFieldName(FieldNames.QUERY); @@ -150,26 +200,36 @@ public HttpRequest buildRequest(Query fql, QueryOptions options, } } - public HttpRequest buildStreamRequest(EventSource eventSource, - StreamOptions streamOptions) { + /** + * Builds and returns an HTTP request for a Fauna stream. + * + * @param eventSource The event source for the stream. + * @param streamOptions The stream options. + * @return An HttpRequest object configured for the Fauna stream. + */ + public HttpRequest buildStreamRequest(final EventSource eventSource, + final StreamOptions streamOptions) { HttpRequest.Builder builder = baseRequestBuilder.copy(); streamOptions.getTimeout().ifPresent(builder::timeout); try { - String body = - new StreamRequest(eventSource, streamOptions).serialize(); - HttpRequest req = - builder.POST(HttpRequest.BodyPublishers.ofString(body)) - .build(); + String body = new StreamRequest(eventSource, streamOptions).serialize(); + HttpRequest req = builder.POST(HttpRequest.BodyPublishers.ofString(body)).build(); logRequest(body, req); return req; } catch (IOException e) { - throw new ClientException("Unable to build Fauna Stream request.", - e); + throw new ClientException("Unable to build Fauna Stream request.", e); } } - public HttpRequest buildFeedRequest(EventSource eventSource, - FeedOptions options) { + /** + * Builds and returns an HTTP request for a Fauna feed. + * + * @param eventSource The event source for the feed. + * @param options The feed options. + * @return An HttpRequest object configured for the Fauna feed. + */ + public HttpRequest buildFeedRequest(final EventSource eventSource, + final FeedOptions options) { FeedRequest request = new FeedRequest(eventSource, options); HttpRequest.Builder builder = baseRequestBuilder.copy(); options.getTimeout().ifPresent(val -> { @@ -179,29 +239,32 @@ public HttpRequest buildFeedRequest(EventSource eventSource, }); try { String body = request.serialize(); - HttpRequest req = builder.POST( - HttpRequest.BodyPublishers.ofString(request.serialize())) - .build(); + HttpRequest req = builder.POST(HttpRequest.BodyPublishers.ofString(request.serialize())).build(); logRequest(body, req); return req; } catch (IOException e) { throw new ClientException("Unable to build Fauna Feed request.", e); } - - } - private static String buildAuthHeader(String token) { + /** + * Builds an authorization header for the given token. + * + * @param token The token to be used in the authorization header. + * @return The authorization header value. + */ + private static String buildAuthHeader(final String token) { return String.join(" ", RequestBuilder.BEARER, token); } /** - * Get either the base request builder (if options is null) or a copy with the options applied. + * Gets the base request builder or a copy with options applied. * - * @param options The QueryOptions (must not be null). + * @param options The QueryOptions (must not be null). + * @param lastTxnTs The last transaction timestamp (optional). + * @return The HttpRequest.Builder configured with options. */ - private HttpRequest.Builder getBuilder(QueryOptions options, - Long lastTxnTs) { + private HttpRequest.Builder getBuilder(final QueryOptions options, final Long lastTxnTs) { if (options == null && (lastTxnTs == null || lastTxnTs <= 0)) { return baseRequestBuilder; } @@ -212,16 +275,13 @@ private HttpRequest.Builder getBuilder(QueryOptions options, if (options != null) { options.getTimeoutMillis().ifPresent(val -> { - builder.timeout( - Duration.ofMillis(val).plus(clientTimeoutBuffer)); + builder.timeout(Duration.ofMillis(val).plus(clientTimeoutBuffer)); builder.header(Headers.QUERY_TIMEOUT_MS, String.valueOf(val)); }); options.getLinearized().ifPresent( - val -> builder.header(Headers.LINEARIZED, - String.valueOf(val))); + val -> builder.header(Headers.LINEARIZED, String.valueOf(val))); options.getTypeCheck().ifPresent( - val -> builder.header(Headers.TYPE_CHECK, - String.valueOf(val))); + val -> builder.header(Headers.TYPE_CHECK, String.valueOf(val))); options.getTraceParent().ifPresent( val -> builder.header(Headers.TRACE_PARENT, val)); options.getQueryTags().ifPresent( diff --git a/src/main/java/com/fauna/client/RetryHandler.java b/src/main/java/com/fauna/client/RetryHandler.java index 07126551..5e0679f1 100644 --- a/src/main/java/com/fauna/client/RetryHandler.java +++ b/src/main/java/com/fauna/client/RetryHandler.java @@ -12,34 +12,56 @@ /** * A retry handler controls the retries for a particular request. + * + * @param The return type for a successful response. */ -public class RetryHandler { +public final class RetryHandler { private final RetryStrategy strategy; private final Logger logger; /** - * Construct a new retry handler instance. + * Constructs a new retry handler instance. * * @param strategy The retry strategy to use. + * @param logger The logger used to log retry details. */ - public RetryHandler(RetryStrategy strategy, Logger logger) { + public RetryHandler(final RetryStrategy strategy, final Logger logger) { this.strategy = strategy; this.logger = logger; } + /** + * Delays the request execution by a specified delay in milliseconds. + * + * @param action The action to be executed. + * @param delayMillis The delay in milliseconds before executing the action. + * @return A CompletableFuture representing the result of the action. + */ public CompletableFuture delayRequest( - Supplier> action, int delayMillis) { + final Supplier> action, final int delayMillis) { return CompletableFuture.supplyAsync( action, CompletableFuture.delayedExecutor(delayMillis, TimeUnit.MILLISECONDS)).join(); } - public static boolean isRetryable(Throwable exc) { + /** + * Checks if an exception is retryable. + * + * @param exc The exception to check. + * @return True if the exception or its cause is retryable. + */ + public static boolean isRetryable(final Throwable exc) { return exc instanceof RetryableException || exc.getCause() instanceof RetryableException; } - public CompletableFuture rethrow(Throwable throwable) { + /** + * Rethrows a throwable as a FaunaException. + * + * @param throwable The throwable to be rethrown. + * @return A failed CompletableFuture containing the throwable. + */ + public CompletableFuture rethrow(final Throwable throwable) { if (throwable instanceof FaunaException) { throw (FaunaException) throwable; } else if (throwable.getCause() instanceof FaunaException) { @@ -48,8 +70,16 @@ public CompletableFuture rethrow(Throwable throwable) { return CompletableFuture.failedFuture(throwable); } - private CompletableFuture retry(Throwable throwable, int retryAttempt, - Supplier> supplier) { + /** + * Retries the action based on the retry strategy. + * + * @param throwable The throwable that caused the failure. + * @param retryAttempt The current retry attempt number. + * @param supplier The action to retry. + * @return A CompletableFuture representing the result of the retried action. + */ + private CompletableFuture retry(final Throwable throwable, final int retryAttempt, + final Supplier> supplier) { try { boolean retryable = isRetryable(throwable); if (retryable && this.strategy.canRetry(retryAttempt)) { @@ -71,10 +101,16 @@ private CompletableFuture retry(Throwable throwable, int retryAttempt, } } - public CompletableFuture execute(Supplier> action) { + /** + * Executes an action with retry logic based on the retry strategy. + * + * @param action The action to execute. + * @return A CompletableFuture representing the result of the action. + */ + public CompletableFuture execute(final Supplier> action) { CompletableFuture f = action.get(); for (int i = 1; i <= this.strategy.getMaxRetryAttempts(); i++) { - int finalI = i; + final int finalI = i; f = f.thenApply(CompletableFuture::completedFuture) .exceptionally(t -> retry(t, finalI, action)) .thenCompose(Function.identity()); diff --git a/src/main/java/com/fauna/client/ScopedFaunaClient.java b/src/main/java/com/fauna/client/ScopedFaunaClient.java index 1e4f99e3..5a0bebda 100644 --- a/src/main/java/com/fauna/client/ScopedFaunaClient.java +++ b/src/main/java/com/fauna/client/ScopedFaunaClient.java @@ -2,16 +2,25 @@ import java.net.http.HttpClient; -public class ScopedFaunaClient extends FaunaClient { +/** + * ScopedFaunaClient is a subclass of FaunaClient that applies a scope to the client, + * limiting the actions and requests to the specified scope. + */ +public final class ScopedFaunaClient extends FaunaClient { private final FaunaClient client; private final RequestBuilder requestBuilder; private final RequestBuilder streamRequestBuilder; private final RequestBuilder feedRequestBuilder; - - public ScopedFaunaClient(FaunaClient client, FaunaScope scope) { + /** + * Constructs a new ScopedFaunaClient using the provided FaunaClient and FaunaScope. + * + * @param client The FaunaClient instance to base the scoped client on. + * @param scope The FaunaScope defining the scope for this client. + */ + public ScopedFaunaClient(final FaunaClient client, final FaunaScope scope) { super(client.getFaunaSecret(), client.getLogger(), - client.getStatsCollector().clone()); + client.getStatsCollector().createNew()); this.client = client; this.requestBuilder = client.getRequestBuilder() .scopedRequestBuilder(scope.getToken(client.getFaunaSecret())); @@ -21,29 +30,53 @@ public ScopedFaunaClient(FaunaClient client, FaunaScope scope) { .scopedRequestBuilder(scope.getToken(client.getFaunaSecret())); } - + /** + * Gets the retry strategy for the scoped client. + * + * @return The retry strategy used by the client. + */ @Override - RetryStrategy getRetryStrategy() { + public RetryStrategy getRetryStrategy() { return client.getRetryStrategy(); } + /** + * Gets the HttpClient used by the scoped client. + * + * @return The HttpClient used for making HTTP requests. + */ @Override - HttpClient getHttpClient() { + public HttpClient getHttpClient() { return client.getHttpClient(); } + /** + * Gets the RequestBuilder for the scoped client. + * + * @return The RequestBuilder used for constructing HTTP requests. + */ @Override - RequestBuilder getRequestBuilder() { + public RequestBuilder getRequestBuilder() { return requestBuilder; } + /** + * Gets the RequestBuilder for streaming requests. + * + * @return The RequestBuilder used for constructing streaming HTTP requests. + */ @Override - RequestBuilder getStreamRequestBuilder() { + public RequestBuilder getStreamRequestBuilder() { return streamRequestBuilder; } + /** + * Gets the RequestBuilder for feed requests. + * + * @return The RequestBuilder used for constructing feed HTTP requests. + */ @Override - RequestBuilder getFeedRequestBuilder() { + public RequestBuilder getFeedRequestBuilder() { return feedRequestBuilder; } } diff --git a/src/main/java/com/fauna/client/StatsCollector.java b/src/main/java/com/fauna/client/StatsCollector.java index c4131b2b..7c9a24e8 100644 --- a/src/main/java/com/fauna/client/StatsCollector.java +++ b/src/main/java/com/fauna/client/StatsCollector.java @@ -26,9 +26,9 @@ public interface StatsCollector { QueryStatsSummary readAndReset(); /** - * Clone the stats collector instance. + * Creates a new instance of a {@code StatsCollector}. * - * @return A clone of the stats collector instance. + * @return A {@code StatsCollector} instance. */ - StatsCollector clone(); + StatsCollector createNew(); } diff --git a/src/main/java/com/fauna/client/StatsCollectorImpl.java b/src/main/java/com/fauna/client/StatsCollectorImpl.java index 490ea890..33e6d99e 100644 --- a/src/main/java/com/fauna/client/StatsCollectorImpl.java +++ b/src/main/java/com/fauna/client/StatsCollectorImpl.java @@ -6,7 +6,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; -public class StatsCollectorImpl implements StatsCollector { +public final class StatsCollectorImpl implements StatsCollector { private static final String RATE_LIMIT_READ_OPS = "read"; private static final String RATE_LIMIT_COMPUTE_OPS = "compute"; @@ -28,7 +28,7 @@ public class StatsCollectorImpl implements StatsCollector { new AtomicInteger(); @Override - public void add(QueryStats stats) { + public void add(final QueryStats stats) { readOps.addAndGet(stats.getReadOps()); computeOps.addAndGet(stats.getComputeOps()); writeOps.addAndGet(stats.getWriteOps()); @@ -50,6 +50,8 @@ public void add(QueryStats stats) { case RATE_LIMIT_WRITE_OPS: rateLimitedWriteQueryCount.incrementAndGet(); break; + default: + break; } }); @@ -93,7 +95,7 @@ public QueryStatsSummary readAndReset() { } @Override - public StatsCollector clone() { + public StatsCollector createNew() { return new StatsCollectorImpl(); } }