From 133f4314793c2b74f97970d257b1c9d25c9ed3de Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 20 Aug 2025 17:12:39 +0000 Subject: [PATCH 1/3] chore(client): refactor closing / shutdown --- .../api/client/okhttp/FinchOkHttpClient.kt | 2 + .../client/okhttp/FinchOkHttpClientAsync.kt | 2 + .../api/client/FinchClientAsyncImpl.kt | 2 +- .../tryfinch/api/client/FinchClientImpl.kt | 2 +- .../com/tryfinch/api/core/ClientOptions.kt | 29 +++++++++- .../core/PhantomReachableExecutorService.kt | 58 +++++++++++++++++++ 6 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 finch-java-core/src/main/kotlin/com/tryfinch/api/core/PhantomReachableExecutorService.kt diff --git a/finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt b/finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt index a41f3cdd..68859894 100644 --- a/finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt +++ b/finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClient.kt @@ -126,6 +126,8 @@ class FinchOkHttpClient private constructor() { * The executor to use for running [AsyncStreamResponse.Handler] callbacks. * * Defaults to a dedicated cached thread pool. + * + * This class takes ownership of the executor and shuts it down, if possible, when closed. */ fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { clientOptions.streamHandlerExecutor(streamHandlerExecutor) diff --git a/finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt b/finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt index 23d05eb1..668500aa 100644 --- a/finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt +++ b/finch-java-client-okhttp/src/main/kotlin/com/tryfinch/api/client/okhttp/FinchOkHttpClientAsync.kt @@ -126,6 +126,8 @@ class FinchOkHttpClientAsync private constructor() { * The executor to use for running [AsyncStreamResponse.Handler] callbacks. * * Defaults to a dedicated cached thread pool. + * + * This class takes ownership of the executor and shuts it down, if possible, when closed. */ fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { clientOptions.streamHandlerExecutor(streamHandlerExecutor) diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientAsyncImpl.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientAsyncImpl.kt index a26bbdbb..fadbac2a 100644 --- a/finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientAsyncImpl.kt +++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientAsyncImpl.kt @@ -200,7 +200,7 @@ class FinchClientAsyncImpl(private val clientOptions: ClientOptions) : FinchClie @get:JsonProperty("provider_id") val providerId: String, ) - override fun close() = clientOptions.httpClient.close() + override fun close() = clientOptions.close() class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : FinchClientAsync.WithRawResponse { diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientImpl.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientImpl.kt index b0d6cd0e..970bf6fc 100644 --- a/finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientImpl.kt +++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/client/FinchClientImpl.kt @@ -189,7 +189,7 @@ class FinchClientImpl(private val clientOptions: ClientOptions) : FinchClient { @get:JsonProperty("provider_id") val providerId: String, ) - override fun close() = clientOptions.httpClient.close() + override fun close() = clientOptions.close() class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) : FinchClient.WithRawResponse { diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/ClientOptions.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/ClientOptions.kt index 6e4b2947..42a6c1e2 100644 --- a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/ClientOptions.kt +++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/ClientOptions.kt @@ -14,6 +14,7 @@ import java.time.Duration import java.util.Base64 import java.util.Optional import java.util.concurrent.Executor +import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import java.util.concurrent.ThreadFactory import java.util.concurrent.atomic.AtomicLong @@ -27,6 +28,8 @@ private constructor( * The HTTP client to use in the SDK. * * Use the one published in `finch-java-client-okhttp` or implement your own. + * + * This class takes ownership of the client and closes it when closed. */ @get:JvmName("httpClient") val httpClient: HttpClient, /** @@ -48,6 +51,8 @@ private constructor( * The executor to use for running [AsyncStreamResponse.Handler] callbacks. * * Defaults to a dedicated cached thread pool. + * + * This class takes ownership of the executor and shuts it down, if possible, when closed. */ @get:JvmName("streamHandlerExecutor") val streamHandlerExecutor: Executor, /** @@ -186,6 +191,8 @@ private constructor( * The HTTP client to use in the SDK. * * Use the one published in `finch-java-client-okhttp` or implement your own. + * + * This class takes ownership of the client and closes it when closed. */ fun httpClient(httpClient: HttpClient) = apply { this.httpClient = PhantomReachableClosingHttpClient(httpClient) @@ -214,9 +221,14 @@ private constructor( * The executor to use for running [AsyncStreamResponse.Handler] callbacks. * * Defaults to a dedicated cached thread pool. + * + * This class takes ownership of the executor and shuts it down, if possible, when closed. */ fun streamHandlerExecutor(streamHandlerExecutor: Executor) = apply { - this.streamHandlerExecutor = streamHandlerExecutor + this.streamHandlerExecutor = + if (streamHandlerExecutor is ExecutorService) + PhantomReachableExecutorService(streamHandlerExecutor) + else streamHandlerExecutor } /** @@ -493,4 +505,19 @@ private constructor( ) } } + + /** + * Closes these client options, relinquishing any underlying resources. + * + * This is purposefully not inherited from [AutoCloseable] because the client options are + * long-lived and usually should not be synchronously closed via try-with-resources. + * + * It's also usually not necessary to call this method at all. the default client automatically + * releases threads and connections if they remain idle, but if you are writing an application + * that needs to aggressively release unused resources, then you may call this method. + */ + fun close() { + httpClient.close() + (streamHandlerExecutor as? ExecutorService)?.shutdown() + } } diff --git a/finch-java-core/src/main/kotlin/com/tryfinch/api/core/PhantomReachableExecutorService.kt b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/PhantomReachableExecutorService.kt new file mode 100644 index 00000000..2a8fbf64 --- /dev/null +++ b/finch-java-core/src/main/kotlin/com/tryfinch/api/core/PhantomReachableExecutorService.kt @@ -0,0 +1,58 @@ +package com.tryfinch.api.core + +import java.util.concurrent.Callable +import java.util.concurrent.ExecutorService +import java.util.concurrent.Future +import java.util.concurrent.TimeUnit + +/** + * A delegating wrapper around an [ExecutorService] that shuts it down once it's only phantom + * reachable. + * + * This class ensures the [ExecutorService] is shut down even if the user forgets to do it. + */ +internal class PhantomReachableExecutorService(private val executorService: ExecutorService) : + ExecutorService { + init { + closeWhenPhantomReachable(this) { executorService.shutdown() } + } + + override fun execute(command: Runnable) = executorService.execute(command) + + override fun shutdown() = executorService.shutdown() + + override fun shutdownNow(): MutableList = executorService.shutdownNow() + + override fun isShutdown(): Boolean = executorService.isShutdown + + override fun isTerminated(): Boolean = executorService.isTerminated + + override fun awaitTermination(timeout: Long, unit: TimeUnit): Boolean = + executorService.awaitTermination(timeout, unit) + + override fun submit(task: Callable): Future = executorService.submit(task) + + override fun submit(task: Runnable, result: T): Future = + executorService.submit(task, result) + + override fun submit(task: Runnable): Future<*> = executorService.submit(task) + + override fun invokeAll( + tasks: MutableCollection> + ): MutableList> = executorService.invokeAll(tasks) + + override fun invokeAll( + tasks: MutableCollection>, + timeout: Long, + unit: TimeUnit, + ): MutableList> = executorService.invokeAll(tasks, timeout, unit) + + override fun invokeAny(tasks: MutableCollection>): T = + executorService.invokeAny(tasks) + + override fun invokeAny( + tasks: MutableCollection>, + timeout: Long, + unit: TimeUnit, + ): T = executorService.invokeAny(tasks, timeout, unit) +} From fb89e608a0822ba5122f9ea5e3fa0fa561b1f388 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 20 Aug 2025 17:51:35 +0000 Subject: [PATCH 2/3] chore(internal): support running formatters directly --- scripts/format | 17 +++++++++++++++-- scripts/java-format | 7 +++++++ scripts/kotlin-format | 7 +++++++ scripts/lint | 17 ++++++++++++++++- 4 files changed, 45 insertions(+), 3 deletions(-) create mode 100755 scripts/java-format create mode 100755 scripts/kotlin-format diff --git a/scripts/format b/scripts/format index 7c0be4d5..65db1769 100755 --- a/scripts/format +++ b/scripts/format @@ -4,5 +4,18 @@ set -e cd "$(dirname "$0")/.." -echo "==> Running formatters" -./gradlew format +if command -v ktfmt &> /dev/null; then + echo "==> Running ktfmt" + ./scripts/kotlin-format +else + echo "==> Running gradlew formatKotlin" + ./gradlew formatKotlin +fi + +if command -v palantir-java-format &> /dev/null; then + echo "==> Running palantir-java-format" + ./scripts/java-format +else + echo "==> Running gradlew formatJava" + ./gradlew formatJava +fi diff --git a/scripts/java-format b/scripts/java-format new file mode 100755 index 00000000..ad5febce --- /dev/null +++ b/scripts/java-format @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +find . -name "*.java" -not -path "./buildSrc/build/*" -print0 | xargs -0 -r palantir-java-format --palantir --replace "$@" diff --git a/scripts/kotlin-format b/scripts/kotlin-format new file mode 100755 index 00000000..3b8be9ea --- /dev/null +++ b/scripts/kotlin-format @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -e + +cd "$(dirname "$0")/.." + +find . -name "*.kt" -not -path "./buildSrc/build/*" -print0 | xargs -0 -r ktfmt --kotlinlang-style "$@" diff --git a/scripts/lint b/scripts/lint index aea8af71..dbc8f776 100755 --- a/scripts/lint +++ b/scripts/lint @@ -5,4 +5,19 @@ set -e cd "$(dirname "$0")/.." echo "==> Running lints" -./gradlew lint + +if command -v ktfmt &> /dev/null; then + echo "==> Checking ktfmt" + ./scripts/kotlin-format --dry-run --set-exit-if-changed +else + echo "==> Running gradlew lintKotlin" + ./gradlew lintKotlin +fi + +if command -v palantir-java-format &> /dev/null; then + echo "==> Checking palantir-java-format" + ./scripts/java-format --dry-run --set-exit-if-changed +else + echo "==> Running gradlew lintJava" + ./gradlew lintJava +fi From 3a949967017e46dc6e4f3621a7bfb77fec5a972c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 20 Aug 2025 17:52:00 +0000 Subject: [PATCH 3/3] release: 7.5.2 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 9 +++++++++ README.md | 10 +++++----- build.gradle.kts | 2 +- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index efc696cf..e49be765 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "7.5.1" + ".": "7.5.2" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 12627a09..c5503aa0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## 7.5.2 (2025-08-20) + +Full Changelog: [v7.5.1...v7.5.2](https://github.com/Finch-API/finch-api-java/compare/v7.5.1...v7.5.2) + +### Chores + +* **client:** refactor closing / shutdown ([133f431](https://github.com/Finch-API/finch-api-java/commit/133f4314793c2b74f97970d257b1c9d25c9ed3de)) +* **internal:** support running formatters directly ([fb89e60](https://github.com/Finch-API/finch-api-java/commit/fb89e608a0822ba5122f9ea5e3fa0fa561b1f388)) + ## 7.5.1 (2025-08-14) Full Changelog: [v7.5.0...v7.5.1](https://github.com/Finch-API/finch-api-java/compare/v7.5.0...v7.5.1) diff --git a/README.md b/README.md index de50ffbe..99f7362a 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ -[![Maven Central](https://img.shields.io/maven-central/v/com.tryfinch.api/finch-java)](https://central.sonatype.com/artifact/com.tryfinch.api/finch-java/7.5.1) -[![javadoc](https://javadoc.io/badge2/com.tryfinch.api/finch-java/7.5.1/javadoc.svg)](https://javadoc.io/doc/com.tryfinch.api/finch-java/7.5.1) +[![Maven Central](https://img.shields.io/maven-central/v/com.tryfinch.api/finch-java)](https://central.sonatype.com/artifact/com.tryfinch.api/finch-java/7.5.2) +[![javadoc](https://javadoc.io/badge2/com.tryfinch.api/finch-java/7.5.2/javadoc.svg)](https://javadoc.io/doc/com.tryfinch.api/finch-java/7.5.2) @@ -15,7 +15,7 @@ It is generated with [Stainless](https://www.stainless.com/). -The REST API documentation can be found on [developer.tryfinch.com](https://developer.tryfinch.com/). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.tryfinch.api/finch-java/7.5.1). +The REST API documentation can be found on [developer.tryfinch.com](https://developer.tryfinch.com/). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.tryfinch.api/finch-java/7.5.2). @@ -26,7 +26,7 @@ The REST API documentation can be found on [developer.tryfinch.com](https://deve ### Gradle ```kotlin -implementation("com.tryfinch.api:finch-java:7.5.1") +implementation("com.tryfinch.api:finch-java:7.5.2") ``` ### Maven @@ -35,7 +35,7 @@ implementation("com.tryfinch.api:finch-java:7.5.1") com.tryfinch.api finch-java - 7.5.1 + 7.5.2 ``` diff --git a/build.gradle.kts b/build.gradle.kts index 41d4588b..7c4ec2b8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ repositories { allprojects { group = "com.tryfinch.api" - version = "7.5.1" // x-release-please-version + version = "7.5.2" // x-release-please-version } subprojects {