Skip to content

Commit

Permalink
Lazily recompute InetAddress subclasses fields at runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
gwenneg committed Nov 20, 2019
1 parent 8be0f98 commit e558604
Show file tree
Hide file tree
Showing 5 changed files with 275 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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<CidrAddress> 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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -19,6 +20,9 @@ public class MicroProfileConfigResource {
@ConfigProperty(name = "microprofile.custom.value")
MicroProfileCustomValue value;

@ConfigProperty(name = "microprofile.cidr-address")
CidrAddress cidrAddress;

@Inject
Config config;

Expand All @@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
}
}

0 comments on commit e558604

Please sign in to comment.