From 1c0c3a7ae4faca1f6f34d9a08ef27572cb5d966a Mon Sep 17 00:00:00 2001 From: Arturo Bernal Date: Sat, 22 Aug 2026 08:45:29 +0200 Subject: [PATCH] Use Brotli4jLoader.isAvailable() instead of relying on class presence. --- .../hc/client5/http/impl/Brotli4jRuntime.java | 28 +++++-- .../http/impl/Brotli4jRuntimeTest.java | 84 +++++++++++++++++++ 2 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 httpclient5/src/test/java/org/apache/hc/client5/http/impl/Brotli4jRuntimeTest.java diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/Brotli4jRuntime.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/Brotli4jRuntime.java index f60b1099f2..a08f8901ee 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/Brotli4jRuntime.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/Brotli4jRuntime.java @@ -35,20 +35,36 @@ @Contract(threading = ThreadingBehavior.STATELESS) public final class Brotli4jRuntime { - private static final String BROTLI = "com.aayushatharva.brotli4j.Brotli4jLoader"; + private static final String BROTLI4J_LOADER = "com.aayushatharva.brotli4j.Brotli4jLoader"; + + private static final String IS_AVAILABLE = "isAvailable"; private Brotli4jRuntime() { } /** - * @return {@code true} if {@code com.aayushatharva.brotli4j} can be loaded - * by the current class loader; {@code false} otherwise + * @return {@code true} if Brotli4j and its native library are available + * to the HttpClient class loader; {@code false} otherwise */ public static boolean available() { + return available(Brotli4jRuntime.class.getClassLoader()); + } + + /** + * Determines whether Brotli4j and its native library are available to the + * given class loader. + * + * @param classLoader the class loader used to locate Brotli4j + * @return {@code true} if Brotli4j reports that its native runtime is + * available; {@code false} otherwise + */ + static boolean available(final ClassLoader classLoader) { try { - Class.forName(BROTLI, false, Brotli4jRuntime.class.getClassLoader()); - return true; - } catch (ClassNotFoundException | LinkageError ex) { + final Class loaderClass = + Class.forName(BROTLI4J_LOADER, true, classLoader); + return Boolean.TRUE.equals( + loaderClass.getMethod(IS_AVAILABLE).invoke(null)); + } catch (final ReflectiveOperationException | LinkageError ex) { return false; } } diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/Brotli4jRuntimeTest.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/Brotli4jRuntimeTest.java new file mode 100644 index 0000000000..23cfd7ddb8 --- /dev/null +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/Brotli4jRuntimeTest.java @@ -0,0 +1,84 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package org.apache.hc.client5.http.impl; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.UUID; + +import com.aayushatharva.brotli4j.Brotli4jLoader; +import com.aayushatharva.brotli4j.service.BrotliNativeProvider; +import org.junit.jupiter.api.Test; + +class Brotli4jRuntimeTest { + + private static final String LIBRARY_PATH_PROPERTY = + "brotli4j.library.path"; + + @Test + void unavailableWhenNativeLibraryCannotBeLoaded() throws Exception { + final URL brotli4jLocation = Brotli4jLoader.class + .getProtectionDomain() + .getCodeSource() + .getLocation(); + final URL serviceLocation = BrotliNativeProvider.class + .getProtectionDomain() + .getCodeSource() + .getLocation(); + + final File missingLibrary = new File( + "target", + "missing-brotli-" + UUID.randomUUID()); + final String previousLibraryPath = System.setProperty( + LIBRARY_PATH_PROPERTY, + missingLibrary.getAbsolutePath()); + + try (URLClassLoader classLoader = new URLClassLoader( + new URL[] {brotli4jLocation, serviceLocation}, + null)) { + + assertNotNull(Class.forName( + "com.aayushatharva.brotli4j.Brotli4jLoader", + false, + classLoader)); + + assertFalse(Brotli4jRuntime.available(classLoader)); + } finally { + if (previousLibraryPath != null) { + System.setProperty( + LIBRARY_PATH_PROPERTY, + previousLibraryPath); + } else { + System.clearProperty(LIBRARY_PATH_PROPERTY); + } + } + } +} \ No newline at end of file