diff --git a/.github/workflows/publish-sonatype.yml b/.github/workflows/publish-sonatype.yml
index de66e5945..1845faf53 100644
--- a/.github/workflows/publish-sonatype.yml
+++ b/.github/workflows/publish-sonatype.yml
@@ -33,7 +33,7 @@ jobs:
export -- GPG_SIGNING_KEY_ID
printenv -- GPG_SIGNING_KEY | gpg --batch --passphrase-fd 3 --import 3<<< "$GPG_SIGNING_PASSWORD"
GPG_SIGNING_KEY_ID="$(gpg --with-colons --list-keys | awk -F : -- '/^pub:/ { getline; print "0x" substr($10, length($10) - 7) }')"
- ./gradlew publishAndReleaseToMavenCentral -Dorg.gradle.jvmargs="-Xmx8g" --stacktrace -PmavenCentralUsername="$SONATYPE_USERNAME" -PmavenCentralPassword="$SONATYPE_PASSWORD" --no-configuration-cache
+ ./gradlew publishAndReleaseToMavenCentral --stacktrace -PmavenCentralUsername="$SONATYPE_USERNAME" -PmavenCentralPassword="$SONATYPE_PASSWORD" --no-configuration-cache
env:
SONATYPE_USERNAME: ${{ secrets.LITHIC_SONATYPE_USERNAME || secrets.SONATYPE_USERNAME }}
SONATYPE_PASSWORD: ${{ secrets.LITHIC_SONATYPE_PASSWORD || secrets.SONATYPE_PASSWORD }}
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index f70d9978a..73033badf 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.102.0"
+ ".": "0.102.1"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index baabb56d9..e56961307 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,16 @@
# Changelog
+## 0.102.1 (2025-08-22)
+
+Full Changelog: [v0.102.0...v0.102.1](https://github.com/lithic-com/lithic-java/compare/v0.102.0...v0.102.1)
+
+### Chores
+
+* **ci:** reduce log noise ([9e55acc](https://github.com/lithic-com/lithic-java/commit/9e55acc8f97887a2a0bb33fd298200fd1d5dc75a))
+* **client:** refactor closing / shutdown ([ffdb0de](https://github.com/lithic-com/lithic-java/commit/ffdb0dec8ae31a8f1a5894ae62c9330d9f5e69fe))
+* **internal:** support running formatters directly ([5803b62](https://github.com/lithic-com/lithic-java/commit/5803b62ba3fa4f6d9cf6f8c92d04f9fd0473a701))
+* remove memory upper bound from publishing step ([e75ad9a](https://github.com/lithic-com/lithic-java/commit/e75ad9a34161e81144bb936ca66113f14ccf659c))
+
## 0.102.0 (2025-08-18)
Full Changelog: [v0.101.0...v0.102.0](https://github.com/lithic-com/lithic-java/compare/v0.101.0...v0.102.0)
diff --git a/README.md b/README.md
index 0af7bfeba..4b78e7961 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,8 @@
-[](https://central.sonatype.com/artifact/com.lithic.api/lithic-java/0.102.0)
-[](https://javadoc.io/doc/com.lithic.api/lithic-java/0.102.0)
+[](https://central.sonatype.com/artifact/com.lithic.api/lithic-java/0.102.1)
+[](https://javadoc.io/doc/com.lithic.api/lithic-java/0.102.1)
@@ -13,7 +13,7 @@ The Lithic Java SDK is similar to the Lithic Kotlin SDK but with minor differenc
-The REST API documentation can be found on [docs.lithic.com](https://docs.lithic.com). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.lithic.api/lithic-java/0.102.0).
+The REST API documentation can be found on [docs.lithic.com](https://docs.lithic.com). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.lithic.api/lithic-java/0.102.1).
@@ -24,7 +24,7 @@ The REST API documentation can be found on [docs.lithic.com](https://docs.lithic
### Gradle
```kotlin
-implementation("com.lithic.api:lithic-java:0.102.0")
+implementation("com.lithic.api:lithic-java:0.102.1")
```
### Maven
@@ -33,7 +33,7 @@ implementation("com.lithic.api:lithic-java:0.102.0")
com.lithic.api
lithic-java
- 0.102.0
+ 0.102.1
```
diff --git a/build.gradle.kts b/build.gradle.kts
index 80c9c68c1..223cdee64 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -8,7 +8,7 @@ repositories {
allprojects {
group = "com.lithic.api"
- version = "0.102.0" // x-release-please-version
+ version = "0.102.1" // x-release-please-version
}
subprojects {
diff --git a/lithic-java-client-okhttp/src/main/kotlin/com/lithic/api/client/okhttp/LithicOkHttpClient.kt b/lithic-java-client-okhttp/src/main/kotlin/com/lithic/api/client/okhttp/LithicOkHttpClient.kt
index 8b454be22..1872c42da 100644
--- a/lithic-java-client-okhttp/src/main/kotlin/com/lithic/api/client/okhttp/LithicOkHttpClient.kt
+++ b/lithic-java-client-okhttp/src/main/kotlin/com/lithic/api/client/okhttp/LithicOkHttpClient.kt
@@ -126,6 +126,8 @@ class LithicOkHttpClient 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/lithic-java-client-okhttp/src/main/kotlin/com/lithic/api/client/okhttp/LithicOkHttpClientAsync.kt b/lithic-java-client-okhttp/src/main/kotlin/com/lithic/api/client/okhttp/LithicOkHttpClientAsync.kt
index 6e2efccdd..82fbba48c 100644
--- a/lithic-java-client-okhttp/src/main/kotlin/com/lithic/api/client/okhttp/LithicOkHttpClientAsync.kt
+++ b/lithic-java-client-okhttp/src/main/kotlin/com/lithic/api/client/okhttp/LithicOkHttpClientAsync.kt
@@ -126,6 +126,8 @@ class LithicOkHttpClientAsync 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/lithic-java-core/src/main/kotlin/com/lithic/api/client/LithicClientAsyncImpl.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/client/LithicClientAsyncImpl.kt
index 173776d11..d0a10d637 100644
--- a/lithic-java-core/src/main/kotlin/com/lithic/api/client/LithicClientAsyncImpl.kt
+++ b/lithic-java-core/src/main/kotlin/com/lithic/api/client/LithicClientAsyncImpl.kt
@@ -288,7 +288,7 @@ class LithicClientAsyncImpl(private val clientOptions: ClientOptions) : LithicCl
// get /v1/status
withRawResponse().apiStatus(params, requestOptions).thenApply { it.parse() }
- override fun close() = clientOptions.httpClient.close()
+ override fun close() = clientOptions.close()
class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) :
LithicClientAsync.WithRawResponse {
diff --git a/lithic-java-core/src/main/kotlin/com/lithic/api/client/LithicClientImpl.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/client/LithicClientImpl.kt
index 3be6e93bf..7be47434e 100644
--- a/lithic-java-core/src/main/kotlin/com/lithic/api/client/LithicClientImpl.kt
+++ b/lithic-java-core/src/main/kotlin/com/lithic/api/client/LithicClientImpl.kt
@@ -270,7 +270,7 @@ class LithicClientImpl(private val clientOptions: ClientOptions) : LithicClient
// get /v1/status
withRawResponse().apiStatus(params, requestOptions).parse()
- override fun close() = clientOptions.httpClient.close()
+ override fun close() = clientOptions.close()
class WithRawResponseImpl internal constructor(private val clientOptions: ClientOptions) :
LithicClient.WithRawResponse {
diff --git a/lithic-java-core/src/main/kotlin/com/lithic/api/core/ClientOptions.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/core/ClientOptions.kt
index 022ff6526..114aecec6 100644
--- a/lithic-java-core/src/main/kotlin/com/lithic/api/core/ClientOptions.kt
+++ b/lithic-java-core/src/main/kotlin/com/lithic/api/core/ClientOptions.kt
@@ -13,6 +13,7 @@ import java.time.Clock
import java.time.Duration
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
@@ -26,6 +27,8 @@ private constructor(
* The HTTP client to use in the SDK.
*
* Use the one published in `lithic-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,
/**
@@ -47,6 +50,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,
/**
@@ -179,6 +184,8 @@ private constructor(
* The HTTP client to use in the SDK.
*
* Use the one published in `lithic-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)
@@ -207,9 +214,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
}
/**
@@ -466,4 +478,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/lithic-java-core/src/main/kotlin/com/lithic/api/core/PhantomReachableExecutorService.kt b/lithic-java-core/src/main/kotlin/com/lithic/api/core/PhantomReachableExecutorService.kt
new file mode 100644
index 000000000..590a30a0e
--- /dev/null
+++ b/lithic-java-core/src/main/kotlin/com/lithic/api/core/PhantomReachableExecutorService.kt
@@ -0,0 +1,58 @@
+package com.lithic.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)
+}
diff --git a/lithic-java-proguard-test/build.gradle.kts b/lithic-java-proguard-test/build.gradle.kts
index 06512330c..d4d64f681 100644
--- a/lithic-java-proguard-test/build.gradle.kts
+++ b/lithic-java-proguard-test/build.gradle.kts
@@ -37,8 +37,6 @@ val proguardJar by tasks.registering(proguard.gradle.ProGuardTask::class) {
outjars(proguardJarPath)
printmapping("${layout.buildDirectory.get()}/proguard-mapping.txt")
- dontwarn()
-
val javaHome = System.getProperty("java.home")
if (System.getProperty("java.version").startsWith("1.")) {
// Before Java 9, the runtime classes were packaged in a single jar file.
diff --git a/lithic-java-proguard-test/test.pro b/lithic-java-proguard-test/test.pro
index 2405d4e26..e72ae1c08 100644
--- a/lithic-java-proguard-test/test.pro
+++ b/lithic-java-proguard-test/test.pro
@@ -5,4 +5,5 @@
-keep class org.junit.** { *; }
# Many warnings don't apply for our testing purposes.
+-dontnote
-dontwarn
\ No newline at end of file
diff --git a/scripts/format b/scripts/format
index 7c0be4d57..65db1769a 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 000000000..ad5febce9
--- /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 000000000..3b8be9ea9
--- /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 aea8af713..dbc8f7760 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