diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c152c54..cc7489a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -14,7 +14,7 @@ on:
jobs:
lint:
- timeout-minutes: 10
+ timeout-minutes: 15
name: lint
runs-on: ${{ github.repository == 'stainless-sdks/brand.dev-java' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
@@ -37,7 +37,7 @@ jobs:
- name: Run lints
run: ./scripts/lint
test:
- timeout-minutes: 10
+ timeout-minutes: 15
name: test
runs-on: ${{ github.repository == 'stainless-sdks/brand.dev-java' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index c373724..46b9b6b 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.1.0-alpha.8"
+ ".": "0.1.0-alpha.9"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 45e6619..f609c47 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,22 @@
# Changelog
+## 0.1.0-alpha.9 (2025-08-08)
+
+Full Changelog: [v0.1.0-alpha.8...v0.1.0-alpha.9](https://github.com/brand-dot-dev/java-sdk/compare/v0.1.0-alpha.8...v0.1.0-alpha.9)
+
+### Bug Fixes
+
+* **client:** r8 support ([ff8057b](https://github.com/brand-dot-dev/java-sdk/commit/ff8057b4355022c580478cd4f185ca407c70cfad))
+
+
+### Chores
+
+* **example:** fix run example comment ([85af0f8](https://github.com/brand-dot-dev/java-sdk/commit/85af0f84d86739f9dc477078df30c6f574fc5c41))
+* increase max gradle JVM heap to 8GB ([8b2f341](https://github.com/brand-dot-dev/java-sdk/commit/8b2f3419961aa90dc92cc9c4f51bae9413613434))
+* **internal:** add async lock helper ([a01460d](https://github.com/brand-dot-dev/java-sdk/commit/a01460d187cb30ba74603da061b98f769ad1e385))
+* **internal:** bump ci test timeout ([27f86a9](https://github.com/brand-dot-dev/java-sdk/commit/27f86a9501ea5d2d3064c7cf3e9452c0a82701eb))
+* **internal:** reduce proguard ci logging ([fd9496e](https://github.com/brand-dot-dev/java-sdk/commit/fd9496e8f38dd306d9afe5d46505b9122ffa81d5))
+
## 0.1.0-alpha.8 (2025-07-23)
Full Changelog: [v0.1.0-alpha.7...v0.1.0-alpha.8](https://github.com/brand-dot-dev/java-sdk/compare/v0.1.0-alpha.7...v0.1.0-alpha.8)
diff --git a/README.md b/README.md
index 5630a60..7ba837b 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,8 @@
-[](https://central.sonatype.com/artifact/com.branddev.api/brand-dev-java/0.1.0-alpha.8)
-[](https://javadoc.io/doc/com.branddev.api/brand-dev-java/0.1.0-alpha.8)
+[](https://central.sonatype.com/artifact/com.branddev.api/brand-dev-java/0.1.0-alpha.9)
+[](https://javadoc.io/doc/com.branddev.api/brand-dev-java/0.1.0-alpha.9)
@@ -13,7 +13,7 @@ It is generated with [Stainless](https://www.stainless.com/).
-Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.branddev.api/brand-dev-java/0.1.0-alpha.8).
+Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.branddev.api/brand-dev-java/0.1.0-alpha.9).
@@ -24,7 +24,7 @@ Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.branddev.api/b
### Gradle
```kotlin
-implementation("com.branddev.api:brand-dev-java:0.1.0-alpha.8")
+implementation("com.branddev.api:brand-dev-java:0.1.0-alpha.9")
```
### Maven
@@ -33,7 +33,7 @@ implementation("com.branddev.api:brand-dev-java:0.1.0-alpha.8")
com.branddev.api
brand-dev-java
- 0.1.0-alpha.8
+ 0.1.0-alpha.9
```
diff --git a/brand-dev-java-core/src/main/kotlin/com/branddev/api/core/Utils.kt b/brand-dev-java-core/src/main/kotlin/com/branddev/api/core/Utils.kt
index 3fdb53d..c91cc5b 100644
--- a/brand-dev-java-core/src/main/kotlin/com/branddev/api/core/Utils.kt
+++ b/brand-dev-java-core/src/main/kotlin/com/branddev/api/core/Utils.kt
@@ -5,6 +5,8 @@ package com.branddev.api.core
import com.branddev.api.errors.BrandDevInvalidDataException
import java.util.Collections
import java.util.SortedMap
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.locks.Lock
@JvmSynthetic
internal fun T?.getOrThrow(name: String): T =
@@ -90,3 +92,24 @@ internal fun Any?.contentToString(): String {
}
internal interface Enum
+
+/**
+ * Executes the given [action] while holding the lock, returning a [CompletableFuture] with the
+ * result.
+ *
+ * @param action The asynchronous action to execute while holding the lock
+ * @return A [CompletableFuture] that completes with the result of the action
+ */
+@JvmSynthetic
+internal fun Lock.withLockAsync(action: () -> CompletableFuture): CompletableFuture {
+ lock()
+ val future =
+ try {
+ action()
+ } catch (e: Throwable) {
+ unlock()
+ throw e
+ }
+ future.whenComplete { _, _ -> unlock() }
+ return future
+}
diff --git a/brand-dev-java-core/src/main/resources/META-INF/proguard/brand-dev-java-core.pro b/brand-dev-java-core/src/main/resources/META-INF/proguard/brand-dev-java-core.pro
index 595d923..0c2cf4e 100644
--- a/brand-dev-java-core/src/main/resources/META-INF/proguard/brand-dev-java-core.pro
+++ b/brand-dev-java-core/src/main/resources/META-INF/proguard/brand-dev-java-core.pro
@@ -1,5 +1,5 @@
# Jackson uses reflection and depends heavily on runtime attributes.
--keepattributes
+-keepattributes Exceptions,InnerClasses,Signature,Deprecated,*Annotation*
# Jackson uses Kotlin reflection utilities, which themselves use reflection to access things.
-keep class kotlin.reflect.** { *; }
@@ -17,13 +17,13 @@
*;
}
-# Jackson uses reflection to access the default constructors of serializers and deserializers.
--keepclassmembers class * extends com.branddev.api.core.BaseSerializer {
- ();
-}
--keepclassmembers class * extends com.branddev.api.core.BaseDeserializer {
- ();
-}
+# Jackson uses reified type information to serialize and deserialize our classes (via `TypeReference`).
+-keep class com.fasterxml.jackson.core.type.TypeReference { *; }
+-keep class * extends com.fasterxml.jackson.core.type.TypeReference { *; }
+
+# Jackson uses reflection to access our class serializers and deserializers.
+-keep @com.fasterxml.jackson.databind.annotation.JsonSerialize class com.branddev.api.** { *; }
+-keep @com.fasterxml.jackson.databind.annotation.JsonDeserialize class com.branddev.api.** { *; }
# Jackson uses reflection to serialize and deserialize our classes based on their constructors and annotated members.
-keepclassmembers class com.branddev.api.** {
diff --git a/brand-dev-java-example/build.gradle.kts b/brand-dev-java-example/build.gradle.kts
index 2ffc061..7011f42 100644
--- a/brand-dev-java-example/build.gradle.kts
+++ b/brand-dev-java-example/build.gradle.kts
@@ -18,7 +18,7 @@ tasks.withType().configureEach {
application {
// Use `./gradlew :brand-dev-java-example:run` to run `Main`
- // Use `./gradlew :brand-dev-java-example:run -Dexample=Something` to run `SomethingExample`
+ // Use `./gradlew :brand-dev-java-example:run -Pexample=Something` to run `SomethingExample`
mainClass = "com.branddev.api.example.${
if (project.hasProperty("example"))
"${project.property("example")}Example"
diff --git a/brand-dev-java-proguard-test/build.gradle.kts b/brand-dev-java-proguard-test/build.gradle.kts
index 901fb98..25bc9a4 100644
--- a/brand-dev-java-proguard-test/build.gradle.kts
+++ b/brand-dev-java-proguard-test/build.gradle.kts
@@ -4,8 +4,13 @@ plugins {
}
buildscript {
+ repositories {
+ google()
+ }
+
dependencies {
classpath("com.guardsquare:proguard-gradle:7.4.2")
+ classpath("com.android.tools:r8:8.3.37")
}
}
@@ -15,7 +20,6 @@ dependencies {
testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3")
testImplementation("org.assertj:assertj-core:3.25.3")
testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.4")
- testImplementation("org.junit.platform:junit-platform-console:1.10.1")
}
tasks.shadowJar {
@@ -33,7 +37,6 @@ val proguardJar by tasks.registering(proguard.gradle.ProGuardTask::class) {
outjars(proguardJarPath)
printmapping("${layout.buildDirectory.get()}/proguard-mapping.txt")
- verbose()
dontwarn()
val javaHome = System.getProperty("java.home")
@@ -58,17 +61,43 @@ val testProGuard by tasks.registering(JavaExec::class) {
dependsOn(proguardJar)
notCompatibleWithConfigurationCache("ProGuard")
- mainClass.set("org.junit.platform.console.ConsoleLauncher")
+ mainClass.set("com.branddev.api.proguard.ProGuardCompatibilityTest")
classpath = files(proguardJarPath)
+}
+
+val r8JarPath = "${layout.buildDirectory.get()}/libs/${project.name}-${project.version}-r8.jar"
+val r8Jar by tasks.registering(JavaExec::class) {
+ group = "verification"
+ dependsOn(tasks.shadowJar)
+ notCompatibleWithConfigurationCache("R8")
+
+ mainClass.set("com.android.tools.r8.R8")
+ classpath = buildscript.configurations["classpath"]
+
args = listOf(
- "--classpath", proguardJarPath,
- "--scan-classpath",
- "--details", "verbose",
+ "--release",
+ "--classfile",
+ "--output", r8JarPath,
+ "--lib", System.getProperty("java.home"),
+ "--pg-conf", "./test.pro",
+ "--pg-conf", "../brand-dev-java-core/src/main/resources/META-INF/proguard/brand-dev-java-core.pro",
+ "--pg-map-output", "${layout.buildDirectory.get()}/r8-mapping.txt",
+ tasks.shadowJar.get().archiveFile.get().asFile.absolutePath,
)
}
+val testR8 by tasks.registering(JavaExec::class) {
+ group = "verification"
+ dependsOn(r8Jar)
+ notCompatibleWithConfigurationCache("R8")
+
+ mainClass.set("com.branddev.api.proguard.ProGuardCompatibilityTest")
+ classpath = files(r8JarPath)
+}
+
tasks.test {
dependsOn(testProGuard)
+ dependsOn(testR8)
// We defer to the tests run via the ProGuard JAR.
enabled = false
}
diff --git a/brand-dev-java-proguard-test/src/test/kotlin/com/branddev/api/proguard/ProGuardCompatibilityTest.kt b/brand-dev-java-proguard-test/src/test/kotlin/com/branddev/api/proguard/ProGuardCompatibilityTest.kt
index 702b584..a3a24c3 100644
--- a/brand-dev-java-proguard-test/src/test/kotlin/com/branddev/api/proguard/ProGuardCompatibilityTest.kt
+++ b/brand-dev-java-proguard-test/src/test/kotlin/com/branddev/api/proguard/ProGuardCompatibilityTest.kt
@@ -6,20 +6,31 @@ import com.branddev.api.client.okhttp.BrandDevOkHttpClient
import com.branddev.api.core.jsonMapper
import com.branddev.api.models.brand.BrandRetrieveResponse
import com.fasterxml.jackson.module.kotlin.jacksonTypeRef
+import kotlin.reflect.full.memberFunctions
+import kotlin.reflect.jvm.javaMethod
import org.assertj.core.api.Assertions.assertThat
-import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
internal class ProGuardCompatibilityTest {
companion object {
- @BeforeAll
@JvmStatic
- fun setUp() {
+ fun main(args: Array) {
// To debug that we're using the right JAR.
val jarPath = this::class.java.getProtectionDomain().codeSource.location
println("JAR being used: $jarPath")
+
+ // We have to manually run the test methods instead of using the JUnit runner because it
+ // seems impossible to get working with R8.
+ val test = ProGuardCompatibilityTest()
+ test::class
+ .memberFunctions
+ .asSequence()
+ .filter { function ->
+ function.javaMethod?.isAnnotationPresent(Test::class.java) == true
+ }
+ .forEach { it.call(test) }
}
}
diff --git a/brand-dev-java-proguard-test/test.pro b/brand-dev-java-proguard-test/test.pro
index e9a70ba..316fd51 100644
--- a/brand-dev-java-proguard-test/test.pro
+++ b/brand-dev-java-proguard-test/test.pro
@@ -2,4 +2,7 @@
-keep class com.branddev.api.proguard.** { *; }
# For the testing framework.
--keep class org.junit.** { *; }
\ No newline at end of file
+-keep class org.junit.** { *; }
+
+# Many warnings don't apply for our testing purposes.
+-dontwarn
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index cd3e3f6..76805bb 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -9,7 +9,7 @@ repositories {
allprojects {
group = "com.branddev.api"
- version = "0.1.0-alpha.8" // x-release-please-version
+ version = "0.1.0-alpha.9" // x-release-please-version
}
subprojects {
diff --git a/gradle.properties b/gradle.properties
index ff76593..6680f9c 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -4,12 +4,13 @@ org.gradle.parallel=true
org.gradle.daemon=false
# These options improve our compilation and test performance. They are inherited by the Kotlin daemon.
org.gradle.jvmargs=\
- -Xms1g \
- -Xmx4g \
+ -Xms2g \
+ -Xmx8g \
-XX:+UseParallelGC \
-XX:InitialCodeCacheSize=256m \
-XX:ReservedCodeCacheSize=1G \
- -XX:MetaspaceSize=256m \
+ -XX:MetaspaceSize=512m \
+ -XX:MaxMetaspaceSize=2G \
-XX:TieredStopAtLevel=1 \
-XX:GCTimeRatio=4 \
-XX:CICompilerCount=4 \