Skip to content

[GR-50683] Initialize most of java.xml at run time. #10537

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.jdk;

import java.util.function.Predicate;

import com.oracle.svm.core.FutureDefaultsOptions;

public class JDKInitializedAtBuildTime implements Predicate<String> {
@Override
public boolean test(String className) {
return !FutureDefaultsOptions.isJDKInitializedAtRunTime();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import java.util.Objects;

import com.oracle.svm.core.jdk.JDKInitializedAtBuildTime;
import org.graalvm.nativeimage.hosted.FieldValueTransformer;

import com.oracle.svm.core.annotate.Alias;
Expand All @@ -45,7 +46,7 @@
* Ideally, we would initialize all of {@code jdk.xml} at run time, but that is too intrusive at the
* current point in time (GR-50683).
*/
@TargetClass(className = "jdk.xml.internal.JdkCatalog", onlyWith = JDKLatest.class)
@TargetClass(className = "jdk.xml.internal.JdkCatalog", onlyWith = {JDKLatest.class, JDKInitializedAtBuildTime.class})
public final class Target_jdk_xml_internal_JdkCatalog {
@Alias //
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Custom, declClass = JdkCatalogSupplier.class, isFinal = true) //
Expand All @@ -62,7 +63,7 @@ public static void init(String resolve) {
final class Target_javax_xml_catalog_Catalog {
}

@TargetClass(className = "javax.xml.catalog.CatalogImpl")
@TargetClass(className = "javax.xml.catalog.CatalogImpl", onlyWith = JDKInitializedAtBuildTime.class)
final class Target_javax_xml_catalog_CatalogImpl {
@Alias //
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) //
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.jdk.xml;

import java.util.Properties;

import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.RecomputeFieldValue;
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.annotate.TargetElement;
import com.oracle.svm.core.jdk.JDK21OrEarlier;
import com.oracle.svm.core.jdk.JDKInitializedAtBuildTime;
import com.oracle.svm.core.jdk.JDKLatest;

import jdk.graal.compiler.serviceprovider.JavaVersionUtil;

@TargetClass(className = "jdk.xml.internal.SecuritySupport", onlyWith = JDKInitializedAtBuildTime.class)
public final class Target_jdk_xml_internal_SecuritySupport {

@Alias //
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias, isFinal = true) //
static Properties cacheProps = new Properties();
@Alias //
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias) //
private static volatile boolean firstTime = true;

@Substitute
public static String readConfig(String propName, boolean stax) {
// always load the default configuration file
if (firstTime) {
synchronized (cacheProps) {
if (firstTime) {
boolean found = XMLSupport.loadJaxpProperties();

// attempts to find stax.properties only if jaxp.properties is not available
if (stax && !found) {
XMLSupport.loadStaxProperties();
}

// load the custom configure on top of the default if any
String configFile = Target_jdk_xml_internal_SecuritySupport
.getSystemProperty(JavaVersionUtil.JAVA_SPEC > 21 ? Target_jdk_xml_internal_JdkConstants.CONFIG_FILE_PROPNAME : Target_jdk_xml_internal_JdkConstants.CONFIG_FILE);
if (configFile != null) {
loadProperties(configFile);
}

firstTime = false;
}
}
}

return cacheProps.getProperty(propName);
}

@Alias
static native boolean loadProperties(String resourceName);

@Alias
public static native String getSystemProperty(final String propName);
}

@TargetClass(className = "jdk.xml.internal.JdkConstants", onlyWith = JDKInitializedAtBuildTime.class)
final class Target_jdk_xml_internal_JdkConstants {
@Alias //
@TargetElement(onlyWith = JDKLatest.class) //
public static String CONFIG_FILE_PROPNAME;

@Alias //
@TargetElement(onlyWith = JDK21OrEarlier.class) //
public static String CONFIG_FILE;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.svm.core.jdk.xml;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.impl.ConfigurationCondition;
import org.graalvm.nativeimage.impl.RuntimeResourceSupport;

import com.oracle.svm.core.jdk.Resources;
import com.oracle.svm.core.util.BasedOnJDKClass;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.util.ReflectionUtil;

@BasedOnJDKClass(className = "jdk.xml.internal.SecuritySupport")
public final class XMLSupport {

private static final Module xmlModule = ReflectionUtil.lookupClass("jdk.xml.internal.SecuritySupport").getModule();
private static final String JAXP_PROPERTIES = "jaxp.properties";
private static final String STAX_PROPERTIES = "stax.properties";

static boolean loadJaxpProperties() {
return loadProperties(JAXP_PROPERTIES);
}

static void loadStaxProperties() {
loadProperties(STAX_PROPERTIES);
}

private static boolean loadProperties(String resource) {
try (InputStream in = Resources.createInputStream(xmlModule, resource)) {
if (in == null) {
return false;
}
Target_jdk_xml_internal_SecuritySupport.cacheProps.load(in);
return true;
} catch (IOException e) {
// shouldn't happen, but required by method Properties.load
throw VMError.shouldNotReachHere(e);
}
}

@Platforms(Platform.HOSTED_ONLY.class)
public static void readProperties() {
byte[] jaxpProperty = readProperties(Paths.get(System.getProperty("java.home"), "conf", JAXP_PROPERTIES)
.toAbsolutePath().normalize());
byte[] staxProperty = jaxpProperty != null ? null
: readProperties(Paths.get(System.getProperty("java.home"), "conf", STAX_PROPERTIES)
.toAbsolutePath().normalize());
addResource(xmlModule, JAXP_PROPERTIES, jaxpProperty);
addResource(xmlModule, STAX_PROPERTIES, staxProperty);
}

@Platforms(Platform.HOSTED_ONLY.class)
public static void addResource(Module module, String resourcePath, byte[] resourceContent) {
if (resourceContent != null) {
RuntimeResourceSupport.singleton().injectResource(module, resourcePath, resourceContent, "Added via " + XMLSupport.class.getName());
} else {
Resources.currentLayer().registerNegativeQuery(module, resourcePath);
}
RuntimeResourceSupport.singleton().addCondition(ConfigurationCondition.alwaysTrue(), module, resourcePath);
}

@Platforms(Platform.HOSTED_ONLY.class)
private static byte[] readProperties(Path path) {
File f = path.toFile();
if (f.exists()) {
try {
return Files.readAllBytes(path);
} catch (IOException e) {
// shouldn't happen, but required by method getFileInputStream
}
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@

import java.lang.reflect.Field;

import com.oracle.svm.core.FutureDefaultsOptions;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport;

import com.oracle.svm.core.FutureDefaultsOptions;
import com.oracle.svm.core.ParsingReason;
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.InternalFeature;
Expand All @@ -53,7 +53,7 @@

@AutomaticallyRegisteredFeature
public class JDKInitializationFeature implements InternalFeature {
private static final String JDK_CLASS_REASON = "Core JDK classes are initialized at build time";
static final String JDK_CLASS_REASON = "Core JDK classes are initialized at build time";

@Override
public void afterRegistration(AfterRegistrationAccess access) {
Expand All @@ -72,8 +72,10 @@ public void afterRegistration(AfterRegistrationAccess access) {
rci.initializeAtBuildTime("java.net", JDK_CLASS_REASON);
rci.initializeAtBuildTime("java.nio", JDK_CLASS_REASON);
rci.initializeAtBuildTime("java.text", JDK_CLASS_REASON);

rci.initializeAtBuildTime("java.time", JDK_CLASS_REASON);
rci.initializeAtRunTime("java.time.chrono.HijrahChronology", "Reads java.home in class initializer.");

rci.initializeAtBuildTime("java.util", JDK_CLASS_REASON);
rci.initializeAtRunTime("java.util.concurrent.SubmissionPublisher", "Executor service must be recomputed");

Expand All @@ -83,7 +85,6 @@ public void afterRegistration(AfterRegistrationAccess access) {
rci.initializeAtBuildTime("javax.naming", JDK_CLASS_REASON);
rci.initializeAtBuildTime("javax.net", JDK_CLASS_REASON);
rci.initializeAtBuildTime("javax.tools", JDK_CLASS_REASON);
rci.initializeAtBuildTime("javax.xml", JDK_CLASS_REASON);

rci.initializeAtBuildTime("jdk.internal", JDK_CLASS_REASON);
rci.initializeAtBuildTime("jdk.jfr", "Needed for Native Image substitutions");
Expand All @@ -94,15 +95,6 @@ public void afterRegistration(AfterRegistrationAccess access) {
rci.initializeAtBuildTime("jdk.net", JDK_CLASS_REASON);
rci.initializeAtBuildTime("jdk.nio", JDK_CLASS_REASON);
rci.initializeAtBuildTime("jdk.vm.ci", "Native Image classes are always initialized at build time");
rci.initializeAtBuildTime("jdk.xml", JDK_CLASS_REASON);
/*
* The XML classes have cyclic class initializer dependencies, and class initialization can
* deadlock/fail when initialization is started at the "wrong part" of the cycle.
* Force-initializing the correct class of the cycle here, in addition to the
* "whole package" initialization above, breaks the cycle because it triggers immediate
* initilalization here before the static analysis is started.
*/
rci.initializeAtBuildTime("jdk.xml.internal.JdkXmlUtils", JDK_CLASS_REASON);

rci.initializeAtBuildTime("sun.invoke", JDK_CLASS_REASON);
rci.initializeAtBuildTime("sun.launcher", JDK_CLASS_REASON);
Expand Down Expand Up @@ -145,11 +137,30 @@ public void afterRegistration(AfterRegistrationAccess access) {

/* XML-related */
if (FutureDefaultsOptions.isJDKInitializedAtRunTime()) {
// GR-50683 should remove this part
rci.initializeAtBuildTime("com.sun.xml", JDK_CLASS_REASON);
rci.initializeAtBuildTime("com.sun.org.apache", JDK_CLASS_REASON);
rci.initializeAtBuildTime("com.sun.org.slf4j.internal", JDK_CLASS_REASON);
/*
* still initialization due to the security services that reach the XMLSecurityManager
*/
rci.initializeAtBuildTime("javax.xml.catalog.CatalogImpl", "Security providers initialized at build time: jdk.xml.internal.XMLSecurityManager");
rci.initializeAtBuildTime("javax.xml.catalog.CatalogResolver$NotFoundAction$1", "Security providers initialized at build time: jdk.xml.internal.XMLSecurityManager");
rci.initializeAtBuildTime("javax.xml.catalog.CatalogResolver$NotFoundAction$2", "Security providers initialized at build time: jdk.xml.internal.XMLSecurityManager");
rci.initializeAtBuildTime("javax.xml.catalog.CatalogResolver$NotFoundAction$3", "Security providers initialized at build time: jdk.xml.internal.XMLSecurityManager");
rci.initializeAtBuildTime("javax.xml.catalog.SystemSuffix", "Security providers initialized at build time: jdk.xml.internal.XMLSecurityManager");
rci.initializeAtBuildTime("javax.xml.catalog.SystemEntry", "Security providers initialized at build time: jdk.xml.internal.XMLSecurityManager");
rci.initializeAtBuildTime("javax.xml.catalog.CatalogFeatures", "Security providers initialized at build time: jdk.xml.internal.XMLSecurityManager");
rci.initializeAtBuildTime("javax.xml.catalog.BaseEntry$CatalogEntryType", "Security providers initialized at build time: jdk.xml.internal.XMLSecurityManager");
rci.initializeAtBuildTime("javax.xml.catalog.CatalogFeatures$State", "Security providers initialized at build time: jdk.xml.internal.XMLSecurityManager");
rci.initializeAtBuildTime("javax.xml.catalog.PublicEntry", "Security providers initialized at build time: jdk.xml.internal.XMLSecurityManager");
} else {
rci.initializeAtBuildTime("jdk.xml", JDK_CLASS_REASON);
/*
* The XML classes have cyclic class initializer dependencies, and class initialization
* can deadlock/fail when initialization is started at the "wrong part" of the cycle.
* Force-initializing the correct class of the cycle here, in addition to the
* "whole package" initialization above, breaks the cycle because it triggers immediate
* initilalization here before the static analysis is started.
*/
rci.initializeAtBuildTime("jdk.xml.internal.JdkXmlUtils", JDK_CLASS_REASON);

rci.initializeAtBuildTime("com.sun.xml", JDK_CLASS_REASON);
rci.initializeAtBuildTime("com.sun.org.apache", JDK_CLASS_REASON);
rci.initializeAtBuildTime("com.sun.org.slf4j.internal", JDK_CLASS_REASON);
Expand Down Expand Up @@ -229,8 +240,6 @@ public void afterRegistration(AfterRegistrationAccess access) {
rci.initializeAtRunTime("jdk.internal.foreign.abi.fallback.FFIABI", "Fails build-time initialization");
rci.initializeAtRunTime("sun.reflect.misc.Trampoline", "Fails build-time initialization");

rci.initializeAtRunTime("com.sun.org.apache.xml.internal.serialize.HTMLdtd", "Fails build-time initialization");

rci.initializeAtRunTime("sun.security.ssl.SSLContextImpl$DefaultSSLContextHolder", "Stores secure random");
rci.initializeAtRunTime("sun.security.ssl.SSLSocketFactoryImpl", "Stores secure random");
rci.initializeAtRunTime("sun.security.provider.certpath.ssl.SSLServerCertStore", "Stores secure random");
Expand Down
Loading