Closed chefchefchef closed 2 months ago
We cannot fix this.
java.util.ServiceLoader.Provider
was added in OpenJDK 9.
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/ServiceLoader.Provider.html
There many components that rely on this, and Jetty is only a small part of this reliance. This is problem outside of our control, and seems to be entirely within Android, or your use of the Android SDK.
Looking at the android source tree, their JDK 11 has the above class. https://android.googlesource.com/platform/libcore/+/refs/tags/jdk11u/jdk-11.0.18+3/src/java.base/share/classes/java/util/ServiceLoader.java#440
Closing, this works for others on Android. You have to configure your project to compile on Java 11. This error only shows up if you haven't done that properly.
I can't get it working any way, using JDK11 and Android SDK33.
I tested for JDK 11 using [this API](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html#isBlank()): https://github.com/zugazagoitia/jett11-android13-mpoc/blob/6bda1e48c7dcaeedde9a88b9d378a5c2ddc0894d/app/src/main/java/com/example/sample/MainActivity.java#L34
You can find a minimal working example here: https://github.com/zugazagoitia/jett11-android13-mpoc
The java.lang.NoClassDefFoundError
is raised as soon as a request is made. Javalin just used another Jetty class to trigger the same result earlier.
The reason, as stated earlier, is clear: the ServiceLoader
class in the android SDK is missing the Provider
interface used by Jetty.
My question is will this be addressed or is it a priority at all?
@zugazagoitia did you open an issue to the Android SDK to add the Provider
interface?
@zugazagoitia did you open an issue to the Android SDK to add the
Provider
interface?
I've just commented on this issue, which seems they were considering.
Update: They implemented the rest of the ServiceLoader
class into the main working tree. This change is however not included in Android 14 (API Level 34) [1][2], likely in a future Android 15 and/or API Level 35 release scheduled for Q3 2024.
This works for me, you can create a new Android module and make sure its build.gradle uses Java 1.8,here we temporarily call it "ponyfill".
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
Then create a placeholder ServiceLoader type under this module (you don’t need to actually implement it), and add the Provider interface definition inside it. Android will always load the system class first when loading a class, so it It will not use the ServiceLoader we used to place the placeholder, but when it cannot find the Provider, it will use the class we provided.
package java.util;
import java.util.function.Supplier;
public final class ServiceLoader<S> {
public interface Provider<S> extends Supplier<S> {
Class<? extends S> type();
S get();
}
}
Finally, you also need to "desugar" Java in your application module
android{
compileOptions {
coreLibraryDesugaringEnabled true
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
}
dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs_nio:2.0.3'
implementation project(":ponyfill")
}
However, things are always full of compromises, and I found a case where Jetty called a method that didn't exist on Android. On Android's non-standard jdk, ServiceLoader has no stream() method no matter what version it is, and Google's "desugar" doesn't handle this, which is frustrating and causes crashes org/eclipse/jetty/util/security/Credential.java Ideally it would be great if Jetty also used ServiceLoaderSpliterator to implement Credential org/eclipse/jetty/util/ServiceLoaderSpliterator.java This is even just a piece of cake, maybe I can raise a PR if needed?
I successfully ran Javalin (depends on Jetty11) 6.0.1 on Android (SDK 33)! Although it is just a simple “Hello World,” it returned as expected.
I embedded Javalin in a JavaFX program and then successfully packaged the APK through the Maven plugin and GraalVM provided by Gluon. It successfully ran on both Android 9 and Android 13.
The entire test project’s code is available at:
Platform: Kali Linux x86 Build Tools: Maven and Gluon Maven Plugin
This works for me, you can create a new Android module and make sure its build.gradle uses Java 1.8,here we temporarily call it "ponyfill".
compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 }
Then create a placeholder ServiceLoader type under this module (you don’t need to actually implement it), and add the Provider interface definition inside it. Android will always load the system class first when loading a class, so it It will not use the ServiceLoader we used to place the placeholder, but when it cannot find the Provider, it will use the class we provided.
package java.util; import java.util.function.Supplier; public final class ServiceLoader<S> { public interface Provider<S> extends Supplier<S> { Class<? extends S> type(); S get(); } }
Finally, you also need to "desugar" Java in your application module
android{ compileOptions { coreLibraryDesugaringEnabled true sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 } } dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs_nio:2.0.3' implementation project(":ponyfill") }
Interesting that this class and method are in desugaring library https://github.com/google/desugar_jdk_libs/tree/master/jdk11/src/java.base/share/classes/java/util
Not sure if desugaring is also applied for the test app (I assume that WireMock is running in context of the test app).
Update: They implemented the rest of the
ServiceLoader
class into the main working tree. This change is however not included in Android 14 (API Level 34) [1][2], likely in a future Android 15 and/or API Level 35 release scheduled for Q3 2024.
Since Android 15 has been released, I think this issue can be closed. @joakime @sbordet
Thanks for the feedback!
Jetty version(s) Jetty 11
Java version/vendor
(use: java -version)
DalvikOS type/version Android SDK 33
Description With Javalin 4 (Jetty 9) and Android I had no problems. After migrating to Javalin 5 (Jetty 11) my server doesn't startup anymore. in JDK 11 I can find the missing class (ServiceLoader$Provider) but in android sdk I cannot (https://developer.android.com/reference/java/util/ServiceLoader). I use Java 11 and compileSdkVersion 33. I cannot find ServiceLoader$Provider in SDK of Android API 33 (when decompiled).
How to reproduce? Run Javalin 5 / Jetty 11 on Android