Skip to content

Commit

Permalink
Tests graalvm-community-jdk21u-issues-28
Browse files Browse the repository at this point in the history
  • Loading branch information
Karm authored and zakkak committed Dec 19, 2024
1 parent 6213684 commit afd9f4c
Show file tree
Hide file tree
Showing 9 changed files with 232 additions and 3 deletions.
39 changes: 39 additions & 0 deletions apps/jdkreflections/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>jdkreflections</groupId>
<artifactId>jdkreflections</artifactId>
<version>1</version>

<name>jdkreflections</name>

<parent>
<groupId>org.graalvm.tests.integration</groupId>
<artifactId>parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

<properties>
<maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
</properties>

<build>
<finalName>jdkreflections</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>${maven-jar-plugin.version}</version>
<configuration>
<archive>
<manifest>
<mainClass>jdkreflections.Main</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
88 changes: 88 additions & 0 deletions apps/jdkreflections/src/main/java/jdkreflections/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright (c) 2024, Red Hat Inc. All rights reserved.
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* Licensed 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.
*
*/
package jdkreflections;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

public class Main {

/**
* An intentionally elaborate way to do this to test the reflection support
* on java.lang.Thread for native-image build configuration.
* @param name
* @return
*/
static ExecutorService createVirtualThreadExecutor(String name) {
try {
final Method virtualThreadBuilderMethod = Arrays.stream(Thread.class.getMethods())
.filter(m -> m.getName().equals("ofVirtual"))
.findAny().orElseThrow();
Object virtualThreadBuilder = virtualThreadBuilderMethod.invoke(null);
final Method nameVirtualThreadBuilderMethod = Arrays.stream(virtualThreadBuilderMethod.getReturnType().getMethods())
.filter(m -> m.getName().equals("name") && m.getParameterCount() == 2)
.findAny().orElseThrow();
virtualThreadBuilder = nameVirtualThreadBuilderMethod.invoke(virtualThreadBuilder, name, 10000L);
final Method factoryVirtualThreadBuilderMethod = Arrays.stream(Class.forName("java.lang.Thread$Builder").getMethods())
.filter(m -> m.getName().equals("factory"))
.findAny().orElseThrow();
final ThreadFactory factory = (ThreadFactory) factoryVirtualThreadBuilderMethod.invoke(virtualThreadBuilder);
final Method newThreadPerTaskExecutorMethod = Arrays.stream(Executors.class.getMethods())
.filter(m -> m.getName().equals("newThreadPerTaskExecutor") && m.getGenericParameterTypes()[0].equals(ThreadFactory.class))
.findAny().orElseThrow();
return (ExecutorService) newThreadPerTaskExecutorMethod.invoke(null, factory);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Fail :-)", e);
}
}

public static void main(String[] args) throws InterruptedException {
final ExecutorService executor = createVirtualThreadExecutor("meh-");
executor.submit(() -> {
try {
final Method currentThreadMethod = Thread.class.getDeclaredMethod("currentThread");
final Thread currentThread = (Thread) currentThreadMethod.invoke(null);
final Method isVirtualMethod = Thread.class.getDeclaredMethod("isVirtual");
final boolean isVirtual = (boolean) isVirtualMethod.invoke(currentThread);
System.out.println("Hello from a " + (isVirtual ? "virtual" : "") + " thread called " + currentThread.getName());
final Field interruptedField = Thread.class.getDeclaredField("interrupted");
interruptedField.setAccessible(true);
System.out.println("interrupted: " + interruptedField.getBoolean(currentThread));
final Method holdsLockMethod = Arrays.stream(Thread.class.getMethods())
.filter(m -> m.getName().equals("holdsLock") && m.getGenericParameterTypes()[0].equals(Object.class))
.findAny().orElseThrow();
System.out.println("holdsLock: " + ((boolean) holdsLockMethod.invoke(null, new Object())));
final Method threadIdMethod = Thread.class.getDeclaredMethod("threadId");
System.out.println("getId: " + ((long) threadIdMethod.invoke(currentThread)));
final Method getNextThreadIdOffsetMethod = Thread.class.getDeclaredMethod("getNextThreadIdOffset");
getNextThreadIdOffsetMethod.setAccessible(true);
System.out.println("getNextThreadIdOffset: " + (long) getNextThreadIdOffsetMethod.invoke(null));
} catch (Exception e) {
e.printStackTrace();
}
});
executor.shutdown();
executor.awaitTermination(1, java.util.concurrent.TimeUnit.SECONDS);
}
}
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
<module>apps/debug-symbols-smoke</module>
<module>apps/helidon-quickstart-se</module>
<module>apps/imageio</module>
<module>apps/jdkreflections</module>
<module>apps/jfr-native-image-performance</module>
<module>apps/quarkus-full-microprofile</module>
<module>apps/quarkus-json</module>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -858,6 +858,63 @@ public void monitorFieldOffsetNOK(TestInfo testInfo, Apps app) throws IOExceptio
}
}

@Test
@Tag("builder-image")
@IfMandrelVersion(minJDK = "21.0.3", inContainer = true)
public void jdkReflectionsContainerTest(TestInfo testInfo) throws IOException, InterruptedException {
jdkReflections(testInfo, Apps.JDK_REFLECTIONS_BUILDER_IMAGE);
}

@Test
@IfMandrelVersion(minJDK = "21.0.3")
public void jdkReflectionsTest(TestInfo testInfo) throws IOException, InterruptedException {
jdkReflections(testInfo, Apps.JDK_REFLECTIONS);
}

public void jdkReflections(TestInfo testInfo, Apps app) throws IOException, InterruptedException {
LOGGER.info("Testing app: " + app);
Process process = null;
File buildLog = null;
File runLog = null;
final StringBuilder report = new StringBuilder();
final File appDir = Path.of(BASE_DIR, app.dir).toFile();
final String cn = testInfo.getTestClass().get().getCanonicalName();
final String mn = testInfo.getTestMethod().get().getName();
final boolean inContainer = app.runtimeContainer != ContainerNames.NONE;
try {
// Cleanup
cleanTarget(app);
if (inContainer) {
removeContainers(app.runtimeContainer.name);
}
Files.createDirectories(Paths.get(appDir.getAbsolutePath() + File.separator + "logs"));
buildLog = Path.of(appDir.getAbsolutePath(), "logs", "build.log").toFile();
runLog = Path.of(appDir.getAbsolutePath(), "logs", "run.log").toFile();
builderRoutine(app, report, cn, mn, appDir, buildLog);
LOGGER.info("Running...");
final List<String> cmd = getRunCommand(app.buildAndRunCmds.runCommands[0]);
process = runCommand(cmd, appDir, runLog, app);
assertNotNull(process, "The test application failed to run. Check " + getLogsDir(cn, mn) + File.separator + buildLog.getName() +
" and also check https://github.com/graalvm/graalvm-community-jdk21u/issues/28");
process.waitFor(5, TimeUnit.SECONDS);
Logs.appendln(report, appDir.getAbsolutePath());
Logs.appendlnSection(report, String.join(" ", cmd));
Logs.checkLog(cn, mn, app, buildLog);
Logs.checkLog(cn, mn, app, runLog);
processStopper(process, true);
final Pattern p = Pattern.compile(".*Hello from a virtual thread called meh-10000.*");
assertTrue(searchLogLines(p, runLog, Charset.defaultCharset()),
"Expected pattern " + p + " was not found in the log. Check " + getLogsDir(cn, mn) + File.separator + runLog.getName() +
" and also check https://github.com/graalvm/graalvm-community-jdk21u/issues/28");
final Pattern p1 = Pattern.compile(".*java.lang.NoSuchMethodException: java.lang.Thread.getNextThreadIdOffset.*");
assertTrue(searchLogLines(p, runLog, Charset.defaultCharset()),
"Expected pattern " + p1 + " was not found in the log. Check " + getLogsDir(cn, mn) + File.separator + runLog.getName() +
". The method getNextThreadIdOffset is deleted from native-image intentionally.");
} finally {
cleanup(process, cn, mn, report, app, buildLog, runLog);
}
}

@Test
@Tag("builder-image")
@IfMandrelVersion(min = "23.1.5", max = "23.1.999", inContainer = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,17 @@ public enum Apps {
URLContent.NONE,
WhitelistLogLines.FOR_SERIALIZATION,
BuildAndRunCmds.FOR_SERIALIZATION_BUILDER_IMAGE,
ContainerNames.FOR_SERIALIZATION_BUILDER_IMAGE);
ContainerNames.FOR_SERIALIZATION_BUILDER_IMAGE),
JDK_REFLECTIONS("apps" + File.separator + "jdkreflections",
URLContent.NONE,
WhitelistLogLines.JDK_REFLECTIONS,
BuildAndRunCmds.JDK_REFLECTIONS,
ContainerNames.NONE),
JDK_REFLECTIONS_BUILDER_IMAGE("apps" + File.separator + "jdkreflections",
URLContent.NONE,
WhitelistLogLines.JDK_REFLECTIONS,
BuildAndRunCmds.JDK_REFLECTIONS_BUILDER_IMAGE,
ContainerNames.JDK_REFLECTIONS_BUILDER_IMAGE);

public final String dir;
public final URLContent urlContent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,32 @@ public enum BuildAndRunCmds {
new String[][] {
{ IS_THIS_WINDOWS ? "target\\timezones.exe" : "./target/timezones" } }
),
JDK_REFLECTIONS(
new String[][] {
{ "mvn", "package" },
{ "java", "--add-opens=java.base/java.lang=ALL-UNNAMED", "-agentlib:native-image-agent=config-output-dir=./target/AGENT",
"-cp", "target/jdkreflections.jar", "jdkreflections.Main" },
{ "native-image", "-J--add-opens=java.base/java.lang=ALL-UNNAMED", "-H:ConfigurationFileDirectories=./target/AGENT",
"--link-at-build-time=", "--no-fallback", "-march=native", "-jar", "target/jdkreflections.jar", "target/jdkreflections" }
},
new String[][] {
{ IS_THIS_WINDOWS ? "target\\jdkreflections.exe" : "./target/jdkreflections" } }
),
JDK_REFLECTIONS_BUILDER_IMAGE(
new String[][] {
{ "mvn", "package" },
{ CONTAINER_RUNTIME, "run", IS_THIS_WINDOWS ? "" : "-u", IS_THIS_WINDOWS ? "" : getUnixUIDGID(),
"-t", "--entrypoint", "java", "-v", BASE_DIR + File.separator + "apps" + File.separator + "jdkreflections:/project:z",
BUILDER_IMAGE, "--add-opens=java.base/java.lang=ALL-UNNAMED",
"-agentlib:native-image-agent=config-output-dir=./target/AGENT", "-cp", "target/jdkreflections.jar", "jdkreflections.Main" },
{ CONTAINER_RUNTIME, "run", IS_THIS_WINDOWS ? "" : "-u", IS_THIS_WINDOWS ? "" : getUnixUIDGID(),
"-v", BASE_DIR + File.separator + "apps" + File.separator + "jdkreflections:/project:z",
BUILDER_IMAGE, "-J--add-opens=java.base/java.lang=ALL-UNNAMED",
"-H:ConfigurationFileDirectories=./target/AGENT", "--link-at-build-time=", "--no-fallback", "-march=native",
"-jar", "target/jdkreflections.jar", "target/jdkreflections" } },
new String[][] {
{ IS_THIS_WINDOWS ? "target\\jdkreflections.exe" : "./target/jdkreflections" } }
),
CALENDARS(
new String[][] {
{ "mvn", "package" },
Expand Down Expand Up @@ -525,7 +551,7 @@ private static String[] hyperfoil() {
}
}

final String[][] buildCommands;
public final String[][] buildCommands;
public final String[][] runCommands;

BuildAndRunCmds(String[][] buildCommands, String[][] runCommands)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ public static Process runCommand(List<String> command, File directory, File logF
if (logFile != null) {
final String c = "Command: " + String.join(" ", command) + "\n";
LOGGER.infof("Command: %s", command);
Files.write(logFile.toPath(), c.getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND);
Files.write(logFile.toPath(), c.getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND, StandardOpenOption.CREATE);
processBuilder.redirectOutput(ProcessBuilder.Redirect.appendTo(logFile));
}
if (input != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public enum ContainerNames {
HYPERFOIL("hyperfoil-container"),
MONITOR_OFFSET_BUILDER_IMAGE("my-monitor-offset-runner"),
FOR_SERIALIZATION_BUILDER_IMAGE("my-for-serialization-runner"),
JDK_REFLECTIONS_BUILDER_IMAGE("my-jdkreflections-runner"),
NONE("NO_CONTAINER");

public final String name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,13 @@ public Pattern[] get(boolean inContainer) {
Pattern.compile(".*sun.reflect.ReflectionFactory is internal proprietary API.*")
};
}
},
JDK_REFLECTIONS {
@Override
public Pattern[] get(boolean inContainer) {
return new Pattern[]{
};
}
};

public abstract Pattern[] get(boolean inContainer);
Expand Down

0 comments on commit afd9f4c

Please sign in to comment.