From 4fadfb9dd514032af9b609d176afdc695a9d41ef Mon Sep 17 00:00:00 2001 From: Georgios Andrianakis Date: Mon, 1 Jun 2020 16:36:29 +0300 Subject: [PATCH] Introduce the ability to generate AppCDS --- .../quarkus/deployment/QuarkusAugmentor.java | 7 +- .../quarkus/deployment/pkg/PackageConfig.java | 25 ++ .../builditem/AppCDSRequestedBuildItem.java | 25 ++ .../pkg/builditem/AppCDSResultBuildItem.java | 24 ++ .../deployment/pkg/steps/AppCDSBuildStep.java | 244 ++++++++++++++++++ .../pkg/steps/JarResultBuildStep.java | 44 +++- .../deployment/steps/MainClassBuildStep.java | 25 +- .../deployment/util/JavaVersionUtil.java | 21 ++ .../io/quarkus/runtime/appcds/AppCDSUtil.java | 28 ++ .../image/jib/deployment/JibProcessor.java | 6 +- 10 files changed, 431 insertions(+), 18 deletions(-) create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/pkg/builditem/AppCDSRequestedBuildItem.java create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/pkg/builditem/AppCDSResultBuildItem.java create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/AppCDSBuildStep.java create mode 100644 core/deployment/src/main/java/io/quarkus/deployment/util/JavaVersionUtil.java create mode 100644 core/runtime/src/main/java/io/quarkus/runtime/appcds/AppCDSUtil.java diff --git a/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java b/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java index 37a24b90c6a30..2546e96ad42d8 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java @@ -10,8 +10,6 @@ import java.util.Properties; import java.util.Set; import java.util.function.Consumer; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import org.eclipse.microprofile.config.spi.ConfigBuilder; import org.eclipse.microprofile.config.spi.ConfigProviderResolver; @@ -37,6 +35,7 @@ import io.quarkus.deployment.pkg.builditem.BuildSystemTargetBuildItem; import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; import io.quarkus.deployment.pkg.builditem.DeploymentResultBuildItem; +import io.quarkus.deployment.util.JavaVersionUtil; import io.quarkus.runtime.LaunchMode; public class QuarkusAugmentor { @@ -78,9 +77,7 @@ public class QuarkusAugmentor { } public BuildResult run() throws Exception { - Pattern pattern = Pattern.compile("(?:1\\.)?(\\d+)(?:\\..*)?"); - Matcher matcher = pattern.matcher(System.getProperty("java.version", "")); - if (matcher.matches() && Integer.parseInt(matcher.group(1)) < 11) { + if (!JavaVersionUtil.isJava11OrHigher()) { log.warn("Using Java versions older than 11 to build" + " Quarkus applications is deprecated and will be disallowed in a future release!"); } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/PackageConfig.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/PackageConfig.java index d1320dff85653..b48eaf80c9ded 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/PackageConfig.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/PackageConfig.java @@ -88,4 +88,29 @@ public class PackageConfig { */ @ConfigItem public Optional outputName; + + /** + * Whether to automate the creation of AppCDS. This has not effect when a native binary is needed and will be ignored in + * that case. + * Furthermore, this options only works for Java 11+. + * Finally, care must be taken to use the same exact JVM version when building and running the application. + */ + @ConfigItem + public boolean createAppcds; + + public boolean isAnyJarType() { + return (type.equalsIgnoreCase(PackageConfig.LEGACY) || + type.equalsIgnoreCase(PackageConfig.JAR) || + type.equalsIgnoreCase(PackageConfig.FAST_JAR) || + type.equalsIgnoreCase(PackageConfig.UBER_JAR)); + } + + public boolean isFastJar() { + return type.equalsIgnoreCase(PackageConfig.FAST_JAR); + } + + public boolean isLegacyJar() { + return (type.equalsIgnoreCase(PackageConfig.LEGACY) || + type.equalsIgnoreCase(PackageConfig.JAR)); + } } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/builditem/AppCDSRequestedBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/builditem/AppCDSRequestedBuildItem.java new file mode 100644 index 0000000000000..054cb5a6132d1 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/builditem/AppCDSRequestedBuildItem.java @@ -0,0 +1,25 @@ +package io.quarkus.deployment.pkg.builditem; + +import java.nio.file.Path; + +import io.quarkus.builder.item.SimpleBuildItem; + +/** + * Build item to notify the indicate to the the various steps that AppCDS generation + * has been requested + */ +public final class AppCDSRequestedBuildItem extends SimpleBuildItem { + + /** + * Directory where various files needed for AppCDS generation will reside + */ + private final Path appCDSDir; + + public AppCDSRequestedBuildItem(Path appCDSDir) { + this.appCDSDir = appCDSDir; + } + + public Path getAppCDSDir() { + return appCDSDir; + } +} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/builditem/AppCDSResultBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/builditem/AppCDSResultBuildItem.java new file mode 100644 index 0000000000000..c603d7fb13a3a --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/builditem/AppCDSResultBuildItem.java @@ -0,0 +1,24 @@ +package io.quarkus.deployment.pkg.builditem; + +import java.nio.file.Path; + +import io.quarkus.builder.item.SimpleBuildItem; + +/** + * A build item containing the result of the AppCDS generation process + */ +public final class AppCDSResultBuildItem extends SimpleBuildItem { + + /** + * The file containing the generated AppCDS + */ + private final Path appCDS; + + public AppCDSResultBuildItem(Path appCDS) { + this.appCDS = appCDS; + } + + public Path getAppCDS() { + return appCDS; + } +} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/AppCDSBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/AppCDSBuildStep.java new file mode 100644 index 0000000000000..e3dca4ae00976 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/AppCDSBuildStep.java @@ -0,0 +1,244 @@ +package io.quarkus.deployment.pkg.steps; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.function.BooleanSupplier; + +import org.jboss.logging.Logger; + +import io.quarkus.bootstrap.util.IoUtils; +import io.quarkus.deployment.annotations.BuildProducer; +import io.quarkus.deployment.annotations.BuildStep; +import io.quarkus.deployment.pkg.PackageConfig; +import io.quarkus.deployment.pkg.builditem.AppCDSRequestedBuildItem; +import io.quarkus.deployment.pkg.builditem.AppCDSResultBuildItem; +import io.quarkus.deployment.pkg.builditem.ArtifactResultBuildItem; +import io.quarkus.deployment.pkg.builditem.JarBuildItem; +import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem; +import io.quarkus.deployment.steps.MainClassBuildStep; +import io.quarkus.deployment.util.JavaVersionUtil; +import io.quarkus.runtime.LaunchMode; + +public class AppCDSBuildStep { + + private static final Logger log = Logger.getLogger(AppCDSBuildStep.class); + public static final String JDK_CLASSLIST_FILE = "classlist"; + public static final String CLASSES_LIST_FILE_NAME = "classes.lst"; + + @BuildStep(onlyIf = AppCDSRequired.class) + public void requested(OutputTargetBuildItem outputTarget, BuildProducer producer) + throws IOException { + Path appCDSDir = outputTarget.getOutputDirectory().resolve("appcds"); + IoUtils.recursiveDelete(appCDSDir); + Files.createDirectories(appCDSDir); + + producer.produce(new AppCDSRequestedBuildItem(outputTarget.getOutputDirectory().resolve("appcds"))); + } + + @BuildStep + public void build(Optional appCDsRequested, + JarBuildItem jarResult, PackageConfig packageConfig, + BuildProducer appCDS, + BuildProducer artifactResult) throws Exception { + if (!appCDsRequested.isPresent()) { + return; + } + + Path appCDSDir = appCDsRequested.get().getAppCDSDir(); + String javaHomeStr = System.getProperty("java.home"); + Path javaHomeDir = Paths.get(javaHomeStr); + Path jdkClassList = javaHomeDir.resolve("lib").resolve(JDK_CLASSLIST_FILE); + if (!jdkClassList.toFile().exists()) { + log.warnf( + "In order to create AppCDS the JDK used to build the Quarkus application must contain a file named '%s' in the its 'lib' directory.", + JDK_CLASSLIST_FILE); + return; + } + String javaExecutableStr = "java"; + String javaBinStr = javaHomeStr + File.separator + "bin" + File.separator + javaExecutableStr; //TODO do we need to add '.exe' here for Windows? + if (!new File(javaBinStr).canExecute()) { + log.warnf( + "In order to create AppCDS the JDK used to build the Quarkus application must contain an executable named '%s' in its 'bin' directory.", + javaBinStr); + return; + } + + Path classesLstPath = createClassesLst(packageConfig, jarResult, javaBinStr, appCDSDir); + if (classesLstPath == null) { + log.warn("Unable to create AppCDS because " + CLASSES_LIST_FILE_NAME + " was not created."); + return; + } + + log.debug("'" + CLASSES_LIST_FILE_NAME + "' successfully created."); + + log.info("Launching AppCDS creation process."); + Path appCDSPath = createAppCDS(jarResult, javaBinStr, classesLstPath, packageConfig.isFastJar()); + if (appCDSPath == null) { + log.warn("Unable to create AppCDS."); + return; + } + + log.infof( + "AppCDS successfully created at: '%s'.\nTo ensure they are loaded properly, " + + "run the application jar from its directory and also add the '-XX:SharedArchiveFile=app-cds.jsa' " + + "JVM flag.\nMoreover, make sure to use the same java version to run the application as was used to build it.", + appCDSPath.toAbsolutePath().toString()); + + appCDS.produce(new AppCDSResultBuildItem(appCDSPath)); + artifactResult.produce(new ArtifactResultBuildItem(appCDSPath, "appCDS", Collections.emptyMap())); + } + + /** + * @return The path of the created classes.lst file or null of the file was not created + */ + private Path createClassesLst(PackageConfig packageConfig, JarBuildItem jarResult, + String javaBin, Path appCDSDir) { + + boolean isFastFar = packageConfig.isFastJar(); + + List command = new ArrayList<>(5); + command.add(javaBin); + command.add("-XX:DumpLoadedClassList=" + CLASSES_LIST_FILE_NAME); + command.add(String.format("-D%s=true", MainClassBuildStep.GENERATE_APP_CDS_SYSTEM_PROPERTY)); + command.add("-jar"); + if (isFastFar) { + command.add(jarResult.getLibraryDir().getParent().resolve(JarResultBuildStep.QUARKUS_RUN_JAR).toAbsolutePath() + .toString()); + } else { + command.add(jarResult.getPath().toAbsolutePath().toString()); + } + + if (log.isDebugEnabled()) { + log.debugf("Launching command: '%s' to create '" + CLASSES_LIST_FILE_NAME + "' AppCDS.", String.join(" ", command)); + } + + int exitCode; + try { + ProcessBuilder processBuilder = new ProcessBuilder(command) + .directory(appCDSDir.toFile()); + if (log.isDebugEnabled()) { + processBuilder.inheritIO(); + } else { + processBuilder.redirectError(NULL_FILE); + processBuilder.redirectOutput(NULL_FILE); + } + exitCode = processBuilder.start().waitFor(); + } catch (Exception e) { + log.debug("Failed to launch process used to create '" + CLASSES_LIST_FILE_NAME + "'.", e); + return null; + } + + if (exitCode != 0) { + log.debugf("The process that was supposed to create AppCDS exited with error code: %d.", exitCode); + return null; + } + + return appCDSDir.resolve(CLASSES_LIST_FILE_NAME); + } + + /** + * @return The path of the created app-cds.jsa file or null of the file was not created + */ + private Path createAppCDS(JarBuildItem jarResult, String javaBin, + Path classesLstPath, boolean isFastFar) { + + Path workingDirectory = isFastFar ? jarResult.getPath().getParent().getParent() : jarResult.getPath().getParent(); + Path appCDSPath = workingDirectory.resolve("app-cds.jsa"); + if (appCDSPath.toFile().exists()) { + try { + Files.delete(appCDSPath); + } catch (IOException e) { + log.debug("Unable to delete existing 'app-cds.jsa' file.", e); + } + } + + List command = new ArrayList<>(6); + command.add(javaBin); + command.add("-Xshare:dump"); + command.add("-XX:SharedClassListFile=" + classesLstPath.toAbsolutePath().toString()); + // We use the relative paths because at runtime 'java -XX:SharedArchiveFile=... -jar ...' expects the AppCDS and jar files + // to match exactly what was used at build time. + // For that reason we also run the creation process from inside the output directory, + // The end result is that users can simply use 'java -XX:SharedArchiveFile=app-cds.jsa -jar app.jar' + command.add("-XX:SharedArchiveFile=" + appCDSPath.getFileName().toString()); + command.add("--class-path"); + if (isFastFar) { + command.add(JarResultBuildStep.QUARKUS_RUN_JAR); + } else { + command.add(jarResult.getPath().getFileName().toString()); + } + + if (log.isDebugEnabled()) { + log.debugf("Launching command: '%s' to create final AppCDS.", String.join(" ", command)); + } + + int exitCode; + try { + ProcessBuilder processBuilder = new ProcessBuilder(command) + .directory(workingDirectory.toFile()); + if (log.isDebugEnabled()) { + processBuilder.inheritIO(); + } else { + processBuilder.redirectError(NULL_FILE); + processBuilder.redirectOutput(NULL_FILE); + } + exitCode = processBuilder.start().waitFor(); + } catch (Exception e) { + log.debug("Failed to launch process used to create AppCDS.", e); + return null; + } + + if (exitCode != 0) { + log.debugf("The process that was supposed to create AppCDS exited with error code: %d.", exitCode); + return null; + } + + if (!appCDSPath.toFile().exists()) { // shouldn't happen, but let's avoid any surprises + return null; + } + + return appCDSPath; + } + + static class AppCDSRequired implements BooleanSupplier { + + private final PackageConfig packageConfig; + private final LaunchMode launchMode; + + AppCDSRequired(PackageConfig packageConfig, LaunchMode launchMode) { + this.packageConfig = packageConfig; + this.launchMode = launchMode; + } + + @Override + public boolean getAsBoolean() { + if (launchMode != LaunchMode.NORMAL) { + return false; + } + + if (!packageConfig.createAppcds || !packageConfig.isAnyJarType()) { + return false; + } + + if (!JavaVersionUtil.isJava11OrHigher()) { + log.warn("AppCDS can only be used with Java 11+."); + return false; + } + return true; + } + } + + // copied from Java 9 + // TODO remove when we move to Java 11 + + private static final File NULL_FILE = new File( + (System.getProperty("os.name") + .startsWith("Windows") ? "NUL" : "/dev/null")); +} diff --git a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/JarResultBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/JarResultBuildStep.java index 5988e3d855a2e..4c117c4522500 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/JarResultBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/JarResultBuildStep.java @@ -5,6 +5,7 @@ import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING; import static java.nio.file.StandardOpenOption.WRITE; +import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.IOException; @@ -24,6 +25,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.function.BiPredicate; import java.util.function.BooleanSupplier; @@ -58,6 +60,7 @@ import io.quarkus.deployment.builditem.QuarkusBuildCloseablesBuildItem; import io.quarkus.deployment.builditem.TransformedClassesBuildItem; import io.quarkus.deployment.pkg.PackageConfig; +import io.quarkus.deployment.pkg.builditem.AppCDSRequestedBuildItem; import io.quarkus.deployment.pkg.builditem.ArtifactResultBuildItem; import io.quarkus.deployment.pkg.builditem.BuildSystemTargetBuildItem; import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; @@ -160,14 +163,18 @@ public JarBuildItem buildRunnerJar(CurateOutcomeBuildItem curateOutcomeBuildItem List generatedResources, List uberJarRequired, QuarkusBuildCloseablesBuildItem closeablesBuildItem, - MainClassBuildItem mainClassBuildItem) throws Exception { + MainClassBuildItem mainClassBuildItem, Optional appCDS) throws Exception { + + if (appCDS.isPresent()) { + handleAppCDSSupportFileGeneration(transformedClasses, generatedClasses, appCDS.get()); + } + if (!uberJarRequired.isEmpty() || packageConfig.uberJar || packageConfig.type.equalsIgnoreCase(PackageConfig.UBER_JAR)) { return buildUberJar(curateOutcomeBuildItem, outputTargetBuildItem, transformedClasses, applicationArchivesBuildItem, packageConfig, applicationInfo, generatedClasses, generatedResources, closeablesBuildItem, mainClassBuildItem); - } else if (packageConfig.type.equalsIgnoreCase(PackageConfig.LEGACY) - || packageConfig.type.equalsIgnoreCase(PackageConfig.JAR)) { + } else if (packageConfig.isLegacyJar()) { return buildLegacyThinJar(curateOutcomeBuildItem, outputTargetBuildItem, transformedClasses, applicationArchivesBuildItem, packageConfig, applicationInfo, generatedClasses, generatedResources, mainClassBuildItem); @@ -177,6 +184,32 @@ public JarBuildItem buildRunnerJar(CurateOutcomeBuildItem curateOutcomeBuildItem } } + // the idea here is to just dump the class names of the generated and transformed classes into a file + // that is read at runtime when AppCDS generation is requested + private void handleAppCDSSupportFileGeneration(TransformedClassesBuildItem transformedClasses, + List generatedClasses, AppCDSRequestedBuildItem appCDS) throws IOException { + Path appCDsDir = appCDS.getAppCDSDir(); + Path generatedClassesFile = appCDsDir.resolve("generatedAndTransformed.lst"); + try (BufferedWriter writer = Files.newBufferedWriter(generatedClassesFile, StandardOpenOption.CREATE)) { + StringBuilder classes = new StringBuilder(); + for (GeneratedClassBuildItem generatedClass : generatedClasses) { + classes.append(generatedClass.getName().replace('/', '.')).append(System.lineSeparator()); + } + + for (Set transformedClassesSet : transformedClasses + .getTransformedClassesByJar().values()) { + for (TransformedClassesBuildItem.TransformedClass transformedClass : transformedClassesSet) { + classes.append(transformedClass.getFileName().replace('/', '.').replace(".class", "")) + .append(System.lineSeparator()); + } + } + + if (classes.length() != 0) { + writer.write(classes.toString()); + } + } + } + private JarBuildItem buildUberJar(CurateOutcomeBuildItem curateOutcomeBuildItem, OutputTargetBuildItem outputTargetBuildItem, TransformedClassesBuildItem transformedClasses, @@ -1097,10 +1130,7 @@ static class JarRequired implements BooleanSupplier { @Override public boolean getAsBoolean() { - return packageConfig.type.equalsIgnoreCase(PackageConfig.LEGACY) || - packageConfig.type.equalsIgnoreCase(PackageConfig.JAR) || - packageConfig.type.equalsIgnoreCase(PackageConfig.FAST_JAR) || - packageConfig.type.equalsIgnoreCase(PackageConfig.UBER_JAR); + return packageConfig.isAnyJarType(); } } diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/MainClassBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/MainClassBuildStep.java index 0d06a11c0e6df..15b5ccb54cca8 100644 --- a/core/deployment/src/main/java/io/quarkus/deployment/steps/MainClassBuildStep.java +++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/MainClassBuildStep.java @@ -50,6 +50,7 @@ import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.configuration.RunTimeConfigurationGenerator; import io.quarkus.deployment.pkg.PackageConfig; +import io.quarkus.deployment.pkg.builditem.AppCDSRequestedBuildItem; import io.quarkus.deployment.recording.BytecodeRecorderImpl; import io.quarkus.dev.appstate.ApplicationStateNotification; import io.quarkus.gizmo.BytecodeCreator; @@ -62,6 +63,7 @@ import io.quarkus.gizmo.ResultHandle; import io.quarkus.gizmo.TryBlock; import io.quarkus.runtime.Application; +import io.quarkus.runtime.ApplicationLifecycleManager; import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.NativeImageRuntimePropertiesRecorder; import io.quarkus.runtime.Quarkus; @@ -69,9 +71,10 @@ import io.quarkus.runtime.StartupContext; import io.quarkus.runtime.StartupTask; import io.quarkus.runtime.annotations.QuarkusMain; +import io.quarkus.runtime.appcds.AppCDSUtil; import io.quarkus.runtime.configuration.ProfileManager; -class MainClassBuildStep { +public class MainClassBuildStep { static final String MAIN_CLASS = "io.quarkus.runner.GeneratedMain"; static final String STARTUP_CONTEXT = "STARTUP_CONTEXT"; @@ -87,6 +90,8 @@ class MainClassBuildStep { JAVAX_NET_SSL_TRUST_STORE_TYPE, JAVAX_NET_SSL_TRUST_STORE_PROVIDER, JAVAX_NET_SSL_TRUST_STORE_PASSWORD)); + public static final String GENERATE_APP_CDS_SYSTEM_PROPERTY = "quarkus.appcds.generate"; + private static final FieldDescriptor STARTUP_CONTEXT_FIELD = FieldDescriptor.of(Application.APP_CLASS_NAME, STARTUP_CONTEXT, StartupContext.class); @@ -102,7 +107,8 @@ void build(List staticInitTasks, BuildProducer generatedClass, LaunchModeBuildItem launchMode, LiveReloadBuildItem liveReloadBuildItem, - ApplicationInfoBuildItem applicationInfo) { + ApplicationInfoBuildItem applicationInfo, + Optional appCDSRequested) { appClassNameProducer.produce(new ApplicationClassNameBuildItem(Application.APP_CLASS_NAME)); @@ -164,6 +170,21 @@ void build(List staticInitTasks, mv = file.getMethodCreator("doStart", void.class, String[].class); mv.setModifiers(Modifier.PROTECTED | Modifier.FINAL); + // if AppCDS generation was requested, we ensure that the application simply loads some classes from a file and terminates + if (appCDSRequested.isPresent()) { + ResultHandle createAppCDsSysProp = mv.invokeStaticMethod( + ofMethod(System.class, "getProperty", String.class, String.class, String.class), + mv.load(GENERATE_APP_CDS_SYSTEM_PROPERTY), mv.load("false")); + ResultHandle createAppCDSBool = mv.invokeStaticMethod( + ofMethod(Boolean.class, "parseBoolean", boolean.class, String.class), createAppCDsSysProp); + BytecodeCreator createAppCDS = mv.ifTrue(createAppCDSBool).trueBranch(); + + createAppCDS.invokeStaticMethod(ofMethod(AppCDSUtil.class, "loadGeneratedClasses", void.class)); + + createAppCDS.invokeStaticMethod(ofMethod(ApplicationLifecycleManager.class, "exit", void.class)); + createAppCDS.returnValue(null); + } + // very first thing is to set system props (for run time, which use substitutions for a different // storage from build-time) for (SystemPropertyBuildItem i : properties) { diff --git a/core/deployment/src/main/java/io/quarkus/deployment/util/JavaVersionUtil.java b/core/deployment/src/main/java/io/quarkus/deployment/util/JavaVersionUtil.java new file mode 100644 index 0000000000000..d7cac86ddc062 --- /dev/null +++ b/core/deployment/src/main/java/io/quarkus/deployment/util/JavaVersionUtil.java @@ -0,0 +1,21 @@ +package io.quarkus.deployment.util; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class JavaVersionUtil { + + private static final Pattern PATTERN = Pattern.compile("(?:1\\.)?(\\d+)(?:\\..*)?"); + + private static boolean IS_JAVA_11_OR_NEWER; + + static { + Matcher matcher = PATTERN.matcher(System.getProperty("java.version", "")); + IS_JAVA_11_OR_NEWER = !matcher.matches() || Integer.parseInt(matcher.group(1)) >= 11; + + } + + public static boolean isJava11OrHigher() { + return IS_JAVA_11_OR_NEWER; + } +} diff --git a/core/runtime/src/main/java/io/quarkus/runtime/appcds/AppCDSUtil.java b/core/runtime/src/main/java/io/quarkus/runtime/appcds/AppCDSUtil.java new file mode 100644 index 0000000000000..6f242cb68f143 --- /dev/null +++ b/core/runtime/src/main/java/io/quarkus/runtime/appcds/AppCDSUtil.java @@ -0,0 +1,28 @@ +package io.quarkus.runtime.appcds; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.Paths; + +public class AppCDSUtil { + + /** + * This is never meant to be used in a regular application run. + * It is only referenced by the generated main with the purpose of + * loading all the generated and transformed classes in order to give the AppCDS + * generation process a larger set of classes to work with + */ + public static void loadGeneratedClasses() throws IOException, ClassNotFoundException { + try (BufferedReader br = new BufferedReader(new FileReader(Paths.get("generatedAndTransformed.lst").toFile()))) { + String line; + while ((line = br.readLine()) != null) { + Class.forName(line, true, Thread.currentThread().getContextClassLoader()); + } + } catch (ClassNotFoundException | IOException e) { + System.err.println("Improperly configured AppCDS generation process launched"); + e.printStackTrace(); + throw e; + } + } +} diff --git a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java b/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java index e2c5d808e1cc0..759e4ac31aa35 100644 --- a/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java +++ b/extensions/container-image/container-image-jib/deployment/src/main/java/io/quarkus/container/image/jib/deployment/JibProcessor.java @@ -89,12 +89,10 @@ public void buildFromJar(ContainerImageConfig containerImageConfig, JibConfig ji JibContainerBuilder jibContainerBuilder; String packageType = packageConfig.type; - if (packageType.equalsIgnoreCase(PackageConfig.LEGACY) - || packageType.equalsIgnoreCase(PackageConfig.JAR) - || packageType.equalsIgnoreCase(PackageConfig.UBER_JAR)) { + if (packageConfig.isLegacyJar() || packageType.equalsIgnoreCase(PackageConfig.UBER_JAR)) { jibContainerBuilder = createContainerBuilderFromLegacyJar(jibConfig, sourceJar, outputTarget, mainClass, containerImageLabels); - } else if (packageType.equalsIgnoreCase(PackageConfig.FAST_JAR)) { + } else if (packageConfig.isFastJar()) { jibContainerBuilder = createContainerBuilderFromFastJar(jibConfig, sourceJar, containerImageLabels); } else { throw new IllegalArgumentException(