Skip to content

Commit

Permalink
Add QuarkusClassLoader#isClassPresentAtRuntime()
Browse files Browse the repository at this point in the history
And use it when we are testing that a class should be available at
runtime.
  • Loading branch information
gsmet committed Apr 16, 2022
1 parent b9ce1e2 commit 52b99f3
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
import io.quarkus.arc.processor.BeanInfo;
import io.quarkus.arc.processor.BuiltinScope;
import io.quarkus.arc.processor.DotNames;
import io.quarkus.bootstrap.classloading.ClassPathElement;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.Capability;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import io.quarkus.arc.deployment.GeneratedBeanBuildItem;
import io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor;
import io.quarkus.arc.deployment.UnremovableBeanBuildItem;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.Capability;
import io.quarkus.deployment.annotations.BuildProducer;
Expand Down Expand Up @@ -231,10 +232,8 @@ void autoRegisterModules(BuildProducer<ClassPathJacksonModuleBuildItem> classPat

private void registerModuleIfOnClassPath(String moduleClassName,
BuildProducer<ClassPathJacksonModuleBuildItem> classPathJacksonModules) {
try {
Class.forName(moduleClassName, false, Thread.currentThread().getContextClassLoader());
if (QuarkusClassLoader.isClassPresentAtRuntime(moduleClassName)) {
classPathJacksonModules.produce(new ClassPathJacksonModuleBuildItem(moduleClassName));
} catch (Exception ignored) {
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@

import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.UnremovableBeanBuildItem;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.Capability;
import io.quarkus.deployment.Feature;
Expand Down Expand Up @@ -177,13 +178,11 @@ void relaxSaslElytron(BuildProducer<RunTimeConfigurationDefaultBuildItem> config
// If elytron is on the classpath and the Kafka connection uses SASL, the Elytron client SASL implementation
// is stricter than what Kafka expects. In this case, configure the SASL client to relax some constraints.
// See https://github.com/quarkusio/quarkus/issues/20088.
try {
Class.forName("org.wildfly.security.sasl.gssapi.AbstractGssapiMechanism", false,
Thread.currentThread().getContextClassLoader());
config.produce(new RunTimeConfigurationDefaultBuildItem("kafka.wildfly.sasl.relax-compliance", "true"));
} catch (Exception e) {
// AbstractGssapiMechanism is not on the classpath, do not set wildfly.sasl.relax-compliance
if (!QuarkusClassLoader.isClassPresentAtRuntime("org.wildfly.security.sasl.gssapi.AbstractGssapiMechanism")) {
return;
}

config.produce(new RunTimeConfigurationDefaultBuildItem("kafka.wildfly.sasl.relax-compliance", "true"));
}

@BuildStep
Expand Down Expand Up @@ -299,41 +298,35 @@ void checkBoostrapServers(KafkaRecorder recorder, Capabilities capabilities) {

private void handleOpenTracing(BuildProducer<ReflectiveClassBuildItem> reflectiveClass, Capabilities capabilities) {
//opentracing contrib kafka interceptors: https://github.com/opentracing-contrib/java-kafka-client
if (capabilities.isPresent(Capability.OPENTRACING)) {
try {
Class.forName("io.opentracing.contrib.kafka.TracingProducerInterceptor", false,
Thread.currentThread().getContextClassLoader());
reflectiveClass.produce(new ReflectiveClassBuildItem(true, true, false,
"io.opentracing.contrib.kafka.TracingProducerInterceptor",
"io.opentracing.contrib.kafka.TracingConsumerInterceptor"));
} catch (ClassNotFoundException e) {
//ignore, opentracing contrib kafka is not in the classpath
}
if (!capabilities.isPresent(Capability.OPENTRACING)
|| !QuarkusClassLoader.isClassPresentAtRuntime("io.opentracing.contrib.kafka.TracingProducerInterceptor")) {
return;
}

reflectiveClass.produce(new ReflectiveClassBuildItem(true, true, false,
"io.opentracing.contrib.kafka.TracingProducerInterceptor",
"io.opentracing.contrib.kafka.TracingConsumerInterceptor"));
}

private void handleStrimziOAuth(BuildProducer<ReflectiveClassBuildItem> reflectiveClass) {
try {
Class.forName("io.strimzi.kafka.oauth.client.JaasClientOauthLoginCallbackHandler", false,
Thread.currentThread().getContextClassLoader());

reflectiveClass.produce(new ReflectiveClassBuildItem(true, true, true,
"io.strimzi.kafka.oauth.client.JaasClientOauthLoginCallbackHandler"));

reflectiveClass.produce(new ReflectiveClassBuildItem(true, true, true,
"org.keycloak.jose.jws.JWSHeader",
"org.keycloak.representations.AccessToken",
"org.keycloak.representations.AccessToken$Access",
"org.keycloak.representations.AccessTokenResponse",
"org.keycloak.representations.IDToken",
"org.keycloak.representations.JsonWebToken",
"org.keycloak.jose.jwk.JSONWebKeySet",
"org.keycloak.jose.jwk.JWK",
"org.keycloak.json.StringOrArrayDeserializer",
"org.keycloak.json.StringListMapDeserializer"));
} catch (ClassNotFoundException e) {
//ignore, Strimzi OAuth Client is not on the classpath
if (!QuarkusClassLoader.isClassPresentAtRuntime("io.strimzi.kafka.oauth.client.JaasClientOauthLoginCallbackHandler")) {
return;
}

reflectiveClass.produce(new ReflectiveClassBuildItem(true, true, true,
"io.strimzi.kafka.oauth.client.JaasClientOauthLoginCallbackHandler"));

reflectiveClass.produce(new ReflectiveClassBuildItem(true, true, true,
"org.keycloak.jose.jws.JWSHeader",
"org.keycloak.representations.AccessToken",
"org.keycloak.representations.AccessToken$Access",
"org.keycloak.representations.AccessTokenResponse",
"org.keycloak.representations.IDToken",
"org.keycloak.representations.JsonWebToken",
"org.keycloak.jose.jwk.JSONWebKeySet",
"org.keycloak.jose.jwk.JWK",
"org.keycloak.json.StringOrArrayDeserializer",
"org.keycloak.json.StringListMapDeserializer"));
}

private void handleAvro(BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
Expand All @@ -344,9 +337,7 @@ private void handleAvro(BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
// Avro - for both Confluent and Apicurio

// --- Confluent ---
try {
Class.forName("io.confluent.kafka.serializers.KafkaAvroDeserializer", false,
Thread.currentThread().getContextClassLoader());
if (QuarkusClassLoader.isClassPresentAtRuntime("io.confluent.kafka.serializers.KafkaAvroDeserializer")) {
reflectiveClass
.produce(new ReflectiveClassBuildItem(true, false,
"io.confluent.kafka.serializers.KafkaAvroDeserializer",
Expand Down Expand Up @@ -381,27 +372,20 @@ private void handleAvro(BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
"io.confluent.kafka.schemaregistry.client.rest.entities.requests.ModeUpdateRequest",
"io.confluent.kafka.schemaregistry.client.rest.entities.requests.RegisterSchemaRequest",
"io.confluent.kafka.schemaregistry.client.rest.entities.requests.RegisterSchemaResponse"));
} catch (ClassNotFoundException e) {
//ignore, Confluent Avro is not in the classpath
}

try {
Class.forName("io.confluent.kafka.schemaregistry.client.security.basicauth.BasicAuthCredentialProvider", false,
Thread.currentThread().getContextClassLoader());
if (QuarkusClassLoader.isClassPresentAtRuntime(
"io.confluent.kafka.schemaregistry.client.security.basicauth.BasicAuthCredentialProvider")) {
serviceProviders
.produce(new ServiceProviderBuildItem(
"io.confluent.kafka.schemaregistry.client.security.basicauth.BasicAuthCredentialProvider",
"io.confluent.kafka.schemaregistry.client.security.basicauth.SaslBasicAuthCredentialProvider",
"io.confluent.kafka.schemaregistry.client.security.basicauth.UrlBasicAuthCredentialProvider",
"io.confluent.kafka.schemaregistry.client.security.basicauth.UserInfoCredentialProvider"));
} catch (ClassNotFoundException e) {
// ignore, Confluent schema registry client not in the classpath
}

// --- Apicurio Registry 1.x ---
try {
Class.forName("io.apicurio.registry.utils.serde.AvroKafkaDeserializer", false,
Thread.currentThread().getContextClassLoader());
if (QuarkusClassLoader.isClassPresentAtRuntime("io.apicurio.registry.utils.serde.AvroKafkaDeserializer")) {
reflectiveClass.produce(
new ReflectiveClassBuildItem(true, true, false,
"io.apicurio.registry.utils.serde.AvroKafkaDeserializer",
Expand All @@ -423,22 +407,13 @@ private void handleAvro(BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
// Apicurio uses dynamic proxies, register them
proxies.produce(new NativeImageProxyDefinitionBuildItem("io.apicurio.registry.client.RegistryService",
"java.lang.AutoCloseable"));

} catch (ClassNotFoundException e) {
// ignore, Apicurio Avro is not in the classpath
}

// --- Apicurio Registry 2.x ---
try {
Class.forName("io.apicurio.registry.serde.avro.AvroKafkaDeserializer", false,
Thread.currentThread().getContextClassLoader());

if (!capabilities.isPresent(Capability.APICURIO_REGISTRY_AVRO)) {
throw new RuntimeException(
"Apicurio Registry 2.x Avro classes detected, please use the quarkus-apicurio-registry-avro extension");
}
} catch (ClassNotFoundException e) {
// ignore, Apicurio Avro is not in the classpath
if (QuarkusClassLoader.isClassPresentAtRuntime("io.apicurio.registry.serde.avro.AvroKafkaDeserializer")
&& !capabilities.isPresent(Capability.APICURIO_REGISTRY_AVRO)) {
throw new RuntimeException(
"Apicurio Registry 2.x Avro classes detected, please use the quarkus-apicurio-registry-avro extension");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static io.quarkus.deployment.builditem.nativeimage.NativeImageResourcePatternsBuildItem.builder;

import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
Expand All @@ -27,11 +28,11 @@ FeatureBuildItem feature() {
*/
@BuildStep
void registerKotlinJacksonModule(BuildProducer<ClassPathJacksonModuleBuildItem> classPathJacksonModules) {
try {
Class.forName(KOTLIN_JACKSON_MODULE, false, Thread.currentThread().getContextClassLoader());
classPathJacksonModules.produce(new ClassPathJacksonModuleBuildItem(KOTLIN_JACKSON_MODULE));
} catch (Exception ignored) {
if (!QuarkusClassLoader.isClassPresentAtRuntime(KOTLIN_JACKSON_MODULE)) {
return;
}

classPathJacksonModules.produce(new ClassPathJacksonModuleBuildItem(KOTLIN_JACKSON_MODULE));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import io.netty.util.internal.logging.InternalLoggerFactory;
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
Expand Down Expand Up @@ -107,59 +108,49 @@ NativeImageConfigBuildItem build(
.addRuntimeInitializedClass("io.netty.buffer.ByteBufUtil")
.addNativeImageSystemProperty("io.netty.leakDetection.level", "DISABLED");

try {
Class.forName("io.netty.handler.codec.http.HttpObjectEncoder");
if (QuarkusClassLoader.isClassPresentAtRuntime("io.netty.handler.codec.http.HttpObjectEncoder")) {
builder
.addRuntimeInitializedClass("io.netty.handler.codec.http.HttpObjectEncoder")
.addRuntimeInitializedClass("io.netty.handler.codec.http.websocketx.extensions.compression.DeflateDecoder")
.addRuntimeInitializedClass("io.netty.handler.codec.http.websocketx.WebSocket00FrameEncoder");
} catch (ClassNotFoundException e) {
//ignore
} else {
log.debug("Not registering Netty HTTP classes as they were not found");
}

try {
Class.forName("io.netty.handler.codec.http2.Http2CodecUtil");
if (QuarkusClassLoader.isClassPresentAtRuntime("io.netty.handler.codec.http2.Http2CodecUtil")) {
builder
.addRuntimeInitializedClass("io.netty.handler.codec.http2.Http2CodecUtil")
.addRuntimeInitializedClass("io.netty.handler.codec.http2.Http2ClientUpgradeCodec")
.addRuntimeInitializedClass("io.netty.handler.codec.http2.DefaultHttp2FrameWriter")
.addRuntimeInitializedClass("io.netty.handler.codec.http2.Http2ConnectionHandler");
} catch (ClassNotFoundException e) {
//ignore
} else {
log.debug("Not registering Netty HTTP2 classes as they were not found");
}

try {
Class.forName("io.netty.channel.unix.UnixChannel");
if (QuarkusClassLoader.isClassPresentAtRuntime("io.netty.channel.unix.UnixChannel")) {
builder.addRuntimeInitializedClass("io.netty.channel.unix.Errors")
.addRuntimeInitializedClass("io.netty.channel.unix.FileDescriptor")
.addRuntimeInitializedClass("io.netty.channel.unix.IovArray")
.addRuntimeInitializedClass("io.netty.channel.unix.Limits");
} catch (ClassNotFoundException e) {
//ignore
} else {
log.debug("Not registering Netty native unix classes as they were not found");
}

try {
Class.forName("io.netty.channel.epoll.EpollMode");
if (QuarkusClassLoader.isClassPresentAtRuntime("io.netty.channel.epoll.EpollMode")) {
builder.addRuntimeInitializedClass("io.netty.channel.epoll.Epoll")
.addRuntimeInitializedClass("io.netty.channel.epoll.EpollEventArray")
.addRuntimeInitializedClass("io.netty.channel.epoll.EpollEventLoop")
.addRuntimeInitializedClass("io.netty.channel.epoll.Native");
} catch (ClassNotFoundException e) {
//ignore
} else {
log.debug("Not registering Netty native epoll classes as they were not found");
}

try {
Class.forName("io.netty.channel.kqueue.AcceptFilter");
if (QuarkusClassLoader.isClassPresentAtRuntime("io.netty.channel.kqueue.AcceptFilter")) {
builder.addRuntimeInitializedClass("io.netty.channel.kqueue.KQueue")
.addRuntimeInitializedClass("io.netty.channel.kqueue.KQueueEventArray")
.addRuntimeInitializedClass("io.netty.channel.kqueue.KQueueEventLoop")
.addRuntimeInitializedClass("io.netty.channel.kqueue.Native");
} catch (ClassNotFoundException e) {
//ignore
} else {
log.debug("Not registering Netty native kqueue classes as they were not found");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem;
import io.quarkus.arc.deployment.InterceptorBindingRegistrarBuildItem;
import io.quarkus.arc.processor.InterceptorBindingRegistrar;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
Expand Down Expand Up @@ -130,11 +131,6 @@ void storeVertxOnContextStorage(OpenTelemetryRecorder recorder, CoreVertxBuildIt
}

public static boolean isClassPresent(String classname) {
try {
Class.forName(classname, false, Thread.currentThread().getContextClassLoader());
return true;
} catch (ClassNotFoundException e) {
return false;
}
return QuarkusClassLoader.isClassPresentAtRuntime(classname);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
import io.quarkus.arc.processor.BuiltinScope;
import io.quarkus.arc.processor.DotNames;
import io.quarkus.arc.processor.Transformation;
import io.quarkus.bootstrap.classloading.ClassPathElement;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
import io.quarkus.arc.processor.DotNames;
import io.quarkus.arc.processor.MethodDescriptors;
import io.quarkus.arc.processor.Types;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.Capabilities;
import io.quarkus.deployment.Capability;
import io.quarkus.deployment.GeneratedClassGizmoAdaptor;
Expand Down Expand Up @@ -1853,9 +1854,7 @@ private void handleReturn(ClassInfo restClientInterface, String defaultMediaType
continuationIndex = parameters.size() - 1;
returnCategory = ReturnCategory.COROUTINE;

try {
Thread.currentThread().getContextClassLoader().loadClass(UNI_KT.toString());
} catch (ClassNotFoundException e) {
if (!QuarkusClassLoader.isClassPresentAtRuntime(UNI_KT.toString())) {
//TODO: make this automatic somehow
throw new RuntimeException("Suspendable rest client method" + jandexMethod + " is present on class "
+ jandexMethod.declaringClass()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkus.scala.deployment;

import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
Expand All @@ -22,10 +23,10 @@ FeatureBuildItem feature() {
*/
@BuildStep
void registerScalaJacksonModule(BuildProducer<ClassPathJacksonModuleBuildItem> classPathJacksonModules) {
try {
Class.forName(SCALA_JACKSON_MODULE, false, Thread.currentThread().getContextClassLoader());
classPathJacksonModules.produce(new ClassPathJacksonModuleBuildItem(SCALA_JACKSON_MODULE));
} catch (Exception ignored) {
if (!QuarkusClassLoader.isClassPresentAtRuntime(SCALA_JACKSON_MODULE)) {
return;
}

classPathJacksonModules.produce(new ClassPathJacksonModuleBuildItem(SCALA_JACKSON_MODULE));
}
}
Loading

0 comments on commit 52b99f3

Please sign in to comment.