Skip to content

Commit

Permalink
Merge pull request #24977 from gsmet/check-resource-runtime
Browse files Browse the repository at this point in the history
Check that resources and classes are effectively available at runtime
  • Loading branch information
gsmet authored Apr 19, 2022
2 parents a8d5279 + 52b99f3 commit eb63ba7
Show file tree
Hide file tree
Showing 12 changed files with 104 additions and 123 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 Expand Up @@ -364,11 +363,8 @@ void optionalResouceBundles(BuildProducer<NativeImageResourceBundleBuildItem> re
AbstractMessageInterpolator.CONTRIBUTOR_VALIDATION_MESSAGES };

for (String potentialHibernateValidatorResourceBundle : potentialHibernateValidatorResourceBundles) {
for (ClassPathElement cpe : QuarkusClassLoader.getElements(potentialHibernateValidatorResourceBundle, false)) {
if (cpe.isRuntime()) {
resourceBundles.produce(new NativeImageResourceBundleBuildItem(potentialHibernateValidatorResourceBundle));
break;
}
if (QuarkusClassLoader.isResourcePresentAtRuntime(potentialHibernateValidatorResourceBundle)) {
resourceBundles.produce(new NativeImageResourceBundleBuildItem(potentialHibernateValidatorResourceBundle));
}
}
}
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 Expand Up @@ -184,10 +183,8 @@ static final class ResteasyConfig {

@BuildStep
NativeImageResourceBundleBuildItem optionalResourceBundle() {
for (ClassPathElement cpe : QuarkusClassLoader.getElements(MESSAGES_RESOURCE_BUNDLE, false)) {
if (cpe.isRuntime()) {
return new NativeImageResourceBundleBuildItem(MESSAGES_RESOURCE_BUNDLE);
}
if (QuarkusClassLoader.isResourcePresentAtRuntime(MESSAGES_RESOURCE_BUNDLE)) {
return new NativeImageResourceBundleBuildItem(MESSAGES_RESOURCE_BUNDLE);
}

return null;
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
Loading

0 comments on commit eb63ba7

Please sign in to comment.