Hi there,
sorry for the wait.
I can tell you how to reproduce it, but I think I now know where the issue come from: the com.google.protobuf:protobuf-java-util library doesn't support "lite".
When I encounter this error I never went past the failing test.
But trying to reproduce the issue in isolation I actually did that and got a different error in my small android app
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:checkDebugDuplicateClasses'.
Caused by: org.gradle.workers.internal.DefaultWorkerExecutor$WorkExecutionException: A failure occurred while executing com.android.build.gradle.internal.tasks.CheckDuplicatesRunnable
Caused by: java.lang.RuntimeException: Duplicate class com.google.protobuf.AbstractMessageLite found in modules protobuf-java-3.21.9 (com.google.protobuf:protobuf-java:3.21.9) and protobuf-javalite-3.21.9 (com.google.protobuf:protobuf-javalite:3.21.9)
Duplicate class com.google.protobuf.AbstractMessageLite$Builder found in modules protobuf-java-3.21.9 (com.google.protobuf:protobuf-java:3.21.9) and protobuf-javalite-3.21.9 (com.google.protobuf:protobuf-javalite:3.21.9)
[...]
this prompt me to look into the dependencies of the utility and I saw it depends on the non-lite version of protobuf.
So I figured: wait, does it mean that if I use the full one instead of lite I won't have the issue?
turns out: yes, if I use the full one I do not have the exceptions above....
foolish by me not to check the dependencies and assuming the utility lib would work for both the lite and full version
Looking again at the exceptions I got from my unit tests:
'com.google.protobuf.Timestamp com.google.protobuf.Timestamp$Builder.build()'
java.lang.NoSuchMethodError: 'com.google.protobuf.Timestamp com.google.protobuf.Timestamp$Builder.build()'
at com.google.protobuf.util.Timestamps.<clinit>(Timestamps.java:73)
at com.example.protolib.ProtobufUtilsKt.toProtoTimestamp(ProtobufUtils.kt:7)
at com.example.protolib.ProtbufUtilsTest.toTimestamp(ProtbufUtilsTest.kt:15)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
Could not initialize class com.google.protobuf.util.Timestamps
java.lang.NoClassDefFoundError: Could not initialize class com.google.protobuf.util.Timestamps
at com.example.protolib.ProtobufUtilsKt.toMillisTimestamp(ProtobufUtils.kt:11)
at com.example.protolib.ProtbufUtilsTest.toMillisTimestamp(ProtbufUtilsTest.kt:25)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
I guess the issue here is that the implementation of Timestamps differ from lite to full protobuf and the util link via reflection to the full one.
Which turn my question in: is there an utility library for the lite version, possibly one that does NOT use reflection?
I implemented the utilities methods I needed myself in my project right now, thinking I would replace it with the official utility when I figured out this issue.
Now I guess I'll have to write my own utilities methods whenever I need to do something.
If you want to reproduce the error this below is the code you need, run the unit test and you'll reproduce the error
and if you switch to the full version (dependencies + comment option lite in the build.gradle.kts file) and run the unit test again it will go away
(I've added some comment)
=========
create a java library module in intellij / android studio called "protolib" and use these codes (with gradle wrapper 8.0)
settings.gradle.kts (this should be fairly standard)
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "ProtobuffUtilIssue"
include(":protolib")
build.gradle.kts
id("com.google.protobuf") version "0.9.1"
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.21.9"
}
generateProtoTasks {
ofSourceSet("main").forEach { task ->
task.builtins {
getByName("java") {
option("lite") // comment for full version
}
id("kotlin") {
option("lite") // comment for full version
}
}
}
}
}
dependencies {
api("com.google.protobuf:protobuf-javalite:3.21.9")
api("com.google.protobuf:protobuf-kotlin-lite:3.21.9")
// swap the above 2 dependencies for these one for full version
// api("com.google.protobuf:protobuf-java:3.21.9")
// api("com.google.protobuf:protobuf-kotlin:3.21.9")
implementation("com.google.protobuf:protobuf-java-util:3.21.9")
testImplementation("junit:junit:4.13.2")
testImplementation("org.jetbrains.kotlin:kotlin-test:1.8.10")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:1.8.10")
}
src/main/java/com/example/protolib/ProtobufUtils.kt
package com.example.protolib
import com.google.protobuf.Timestamp
import com.google.protobuf.util.Timestamps
fun Long.toProtoTimestamp(): Timestamp {
return Timestamps.fromMillis(this)
}
fun Timestamp.toMillisTimestamp(): Long {
return Timestamps.toMillis(this)
}
src/test/java/com/example/protolib/ProtbufUtilsTest.kt (unit test!)
package com.example.protolib
import com.google.protobuf.timestamp
import kotlin.test.Test
import kotlin.test.assertEquals
class ProtbufUtilsTest {
@Test
fun toTimestamp() {
val timestampLong = 1668178299L
val expected = timestamp {
seconds = 1668178
nanos = 299000000
}
assertEquals(expected, timestampLong.toProtoTimestamp())
}
@Test
fun toMillisTimestamp() {
val timestamp = timestamp {
seconds = 1668178
nanos = 299000000
}
val expected = 1668178299L
assertEquals(expected, timestamp.toMillisTimestamp())
}
}
========