diff --git a/core/runtime/src/main/java/io/quarkus/runtime/graal/CidrAddressSubstitutions.java b/core/runtime/src/main/java/io/quarkus/runtime/graal/CidrAddressSubstitutions.java new file mode 100644 index 00000000000000..d9cb7b0fa2780e --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/runtime/graal/CidrAddressSubstitutions.java @@ -0,0 +1,93 @@ +package io.quarkus.runtime.graal; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.net.InetAddress; + +import org.wildfly.common.net.CidrAddress; +import org.wildfly.common.net.Inet; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.InjectAccessors; +import com.oracle.svm.core.annotate.TargetClass; + +/* + * The following substitutions are required because of a new restriction in GraalVM 19.3.0 that prohibits the presence of + * java.net.Inet4Address and java.net.Inet6Address in the image heap. Each field annotated with @InjectAccessors is lazily + * recomputed at runtime on first access while CidrAddress.class can still be initialized during the native image build. + */ +@TargetClass(CidrAddress.class) +final class Target_org_wildfly_common_net_CidrAddress { + + @Alias + @InjectAccessors(Inet4AnyCidrAccessor.class) + public static CidrAddress INET4_ANY_CIDR; + + @Alias + @InjectAccessors(Inet6AnyCidrAccessor.class) + public static CidrAddress INET6_ANY_CIDR; +} + +class Inet4AnyCidrAccessor { + + private static volatile CidrAddress INET4_ANY_CIDR; + + static CidrAddress get() { + CidrAddress result = INET4_ANY_CIDR; + if (result == null) { + // Lazy initialization on first access. + result = initializeOnce(); + } + return result; + } + + private static synchronized CidrAddress initializeOnce() { + CidrAddress result = INET4_ANY_CIDR; + if (result != null) { + // Double-checked locking is OK because INSTANCE is volatile. + return result; + } + result = CidrAddressUtil.newInstance(Inet.INET4_ANY, 0); + INET4_ANY_CIDR = result; + return result; + } +} + +class Inet6AnyCidrAccessor { + + private static volatile CidrAddress INET6_ANY_CIDR; + + static CidrAddress get() { + CidrAddress result = INET6_ANY_CIDR; + if (result == null) { + // Lazy initialization on first access. + result = initializeOnce(); + } + return result; + } + + private static synchronized CidrAddress initializeOnce() { + CidrAddress result = INET6_ANY_CIDR; + if (result != null) { + // Double-checked locking is OK because INSTANCE is volatile. + return result; + } + result = CidrAddressUtil.newInstance(Inet.INET6_ANY, 0); + INET6_ANY_CIDR = result; + return result; + } +} + +class CidrAddressUtil { + + public static CidrAddress newInstance(InetAddress networkAddress, int netmaskBits) { + try { + Constructor ctor = CidrAddress.class.getDeclaredConstructor(InetAddress.class, int.class); + ctor.setAccessible(true); + return ctor.newInstance(networkAddress, netmaskBits); + } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException + | IllegalArgumentException | InvocationTargetException e) { + throw new RuntimeException("CidrAddress instanciation failed", e); + } + } +} diff --git a/core/runtime/src/main/java/io/quarkus/runtime/graal/InetSubstitutions.java b/core/runtime/src/main/java/io/quarkus/runtime/graal/InetSubstitutions.java new file mode 100644 index 00000000000000..5cb44b3d8d56db --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/runtime/graal/InetSubstitutions.java @@ -0,0 +1,164 @@ +package io.quarkus.runtime.graal; + +import java.net.Inet4Address; +import java.net.Inet6Address; + +import org.wildfly.common.net.Inet; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.InjectAccessors; +import com.oracle.svm.core.annotate.TargetClass; + +/* + * The following substitutions are required because of a new restriction in GraalVM 19.3.0 that prohibits the presence of + * java.net.Inet4Address and java.net.Inet6Address in the image heap. Each field annotated with @InjectAccessors is lazily + * recomputed at runtime on first access while Inet.class can still be initialized during the native image build. + */ +@TargetClass(Inet.class) +final class Target_org_wildfly_common_net_Inet { + + @Alias + @InjectAccessors(Inet4AnyAccessor.class) + public static Inet4Address INET4_ANY; + + @Alias + @InjectAccessors(Inet4LoopbackAccessor.class) + public static Inet4Address INET4_LOOPBACK; + + @Alias + @InjectAccessors(Inet4BroadcastAccessor.class) + public static Inet4Address INET4_BROADCAST; + + @Alias + @InjectAccessors(Inet6AnyAccessor.class) + public static Inet6Address INET6_ANY; + + @Alias + @InjectAccessors(Inet6LoopbackAccessor.class) + public static Inet6Address INET6_LOOPBACK; +} + +class Inet4AnyAccessor { + + private static volatile Inet4Address INET4_ANY; + + static Inet4Address get() { + Inet4Address result = INET4_ANY; + if (result == null) { + // Lazy initialization on first access. + result = initializeOnce(); + } + return result; + } + + private static synchronized Inet4Address initializeOnce() { + Inet4Address result = INET4_ANY; + if (result != null) { + // Double-checked locking is OK because INSTANCE is volatile. + return result; + } + result = Inet.getInet4Address(0, 0, 0, 0); + INET4_ANY = result; + return result; + } +} + +class Inet4LoopbackAccessor { + + private static volatile Inet4Address INET4_LOOPBACK; + + static Inet4Address get() { + Inet4Address result = INET4_LOOPBACK; + if (result == null) { + // Lazy initialization on first access. + result = initializeOnce(); + } + return result; + } + + private static synchronized Inet4Address initializeOnce() { + Inet4Address result = INET4_LOOPBACK; + if (result != null) { + // Double-checked locking is OK because INSTANCE is volatile. + return result; + } + result = Inet.getInet4Address(127, 0, 0, 1); + INET4_LOOPBACK = result; + return result; + } +} + +class Inet4BroadcastAccessor { + + private static volatile Inet4Address INET4_BROADCAST; + + static Inet4Address get() { + Inet4Address result = INET4_BROADCAST; + if (result == null) { + // Lazy initialization on first access. + result = initializeOnce(); + } + return result; + } + + private static synchronized Inet4Address initializeOnce() { + Inet4Address result = INET4_BROADCAST; + if (result != null) { + // Double-checked locking is OK because INSTANCE is volatile. + return result; + } + result = Inet.getInet4Address(255, 255, 255, 255); + INET4_BROADCAST = result; + return result; + } +} + +class Inet6AnyAccessor { + + private static volatile Inet6Address INET6_ANY; + + static Inet6Address get() { + Inet6Address result = INET6_ANY; + if (result == null) { + // Lazy initialization on first access. + result = initializeOnce(); + } + return result; + } + + private static synchronized Inet6Address initializeOnce() { + Inet6Address result = INET6_ANY; + if (result != null) { + // Double-checked locking is OK because INSTANCE is volatile. + return result; + } + result = Inet.getInet6Address(0, 0, 0, 0, 0, 0, 0, 0); + INET6_ANY = result; + return result; + } +} + +class Inet6LoopbackAccessor { + + private static volatile Inet6Address INET6_LOOPBACK; + + static Inet6Address get() { + Inet6Address result = INET6_LOOPBACK; + if (result == null) { + // Lazy initialization on first access. + result = initializeOnce(); + } + return result; + } + + private static synchronized Inet6Address initializeOnce() { + Inet6Address result = INET6_LOOPBACK; + if (result != null) { + // Double-checked locking is OK because INSTANCE is volatile. + return result; + } + result = Inet.getInet6Address(0, 0, 0, 0, 0, 0, 0, 1); + INET6_LOOPBACK = result; + return result; + } +} diff --git a/integration-tests/main/src/main/java/io/quarkus/it/config/MicroProfileConfigResource.java b/integration-tests/main/src/main/java/io/quarkus/it/config/MicroProfileConfigResource.java index 09f2080536b50f..02c0359d6dfa9d 100644 --- a/integration-tests/main/src/main/java/io/quarkus/it/config/MicroProfileConfigResource.java +++ b/integration-tests/main/src/main/java/io/quarkus/it/config/MicroProfileConfigResource.java @@ -6,6 +6,7 @@ import org.eclipse.microprofile.config.Config; import org.eclipse.microprofile.config.inject.ConfigProperty; +import org.wildfly.common.net.CidrAddress; /** * Test some MicroProfile config primitives. @@ -19,6 +20,9 @@ public class MicroProfileConfigResource { @ConfigProperty(name = "microprofile.custom.value") MicroProfileCustomValue value; + @ConfigProperty(name = "microprofile.cidr-address") + CidrAddress cidrAddress; + @Inject Config config; @@ -36,4 +40,10 @@ public String getPropertyNames() throws Exception { public String getCustomValue() { return Integer.toString(value.getNumber()); } + + @GET + @Path("/get-cidr-address") + public String getCidrAddress() { + return cidrAddress.getNetworkAddress().getHostAddress(); + } } diff --git a/integration-tests/main/src/main/resources/application.properties b/integration-tests/main/src/main/resources/application.properties index bf4bcb937599d6..8ef8a9baa88ed0 100644 --- a/integration-tests/main/src/main/resources/application.properties +++ b/integration-tests/main/src/main/resources/application.properties @@ -26,6 +26,7 @@ names=quarkus,redhat schedulerservice.cron.expr=0/10 * * * * ? microprofile.custom.value = 456 +microprofile.cidr-address=10.0.0.20/24 configproperties.message=Hello configproperties.other=1 diff --git a/integration-tests/main/src/test/java/io/quarkus/it/main/MicroProfileConfigTestCase.java b/integration-tests/main/src/test/java/io/quarkus/it/main/MicroProfileConfigTestCase.java index 4c181d8ba3154e..94ef2e6c059058 100644 --- a/integration-tests/main/src/test/java/io/quarkus/it/main/MicroProfileConfigTestCase.java +++ b/integration-tests/main/src/test/java/io/quarkus/it/main/MicroProfileConfigTestCase.java @@ -25,4 +25,11 @@ public void testMicroprofileConfigGetCustomValue() { .when().get("/microprofile-config/get-custom-value") .then().body(is("456")); } + + @Test + public void testCidrAddress() { + RestAssured + .when().get("/microprofile-config/get-cidr-address") + .then().body(is("10.0.0.0")); + } }