Skip to content

Commit 7922ad6

Browse files
committed
Initialize most of java.xml at run time
1 parent 9bde10c commit 7922ad6

File tree

5 files changed

+265
-20
lines changed

5 files changed

+265
-20
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core.jdk;
26+
27+
import java.util.function.Predicate;
28+
29+
import com.oracle.svm.core.FutureDefaultsOptions;
30+
31+
public class JDKInitializedAtBuildTime implements Predicate<String> {
32+
@Override
33+
public boolean test(String className) {
34+
return !FutureDefaultsOptions.isJDKInitializedAtRunTime();
35+
}
36+
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/xml/Target_jdk_xml_internal_JdkCatalog.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import java.util.Objects;
2828

29+
import com.oracle.svm.core.jdk.JDKInitializedAtBuildTime;
2930
import org.graalvm.nativeimage.hosted.FieldValueTransformer;
3031

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

65-
@TargetClass(className = "javax.xml.catalog.CatalogImpl")
66+
@TargetClass(className = "javax.xml.catalog.CatalogImpl", onlyWith = JDKInitializedAtBuildTime.class)
6667
final class Target_javax_xml_catalog_CatalogImpl {
6768
@Alias //
6869
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) //
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core.jdk.xml;
26+
27+
import java.util.Properties;
28+
29+
import com.oracle.svm.core.annotate.Alias;
30+
import com.oracle.svm.core.annotate.RecomputeFieldValue;
31+
import com.oracle.svm.core.annotate.Substitute;
32+
import com.oracle.svm.core.annotate.TargetClass;
33+
import com.oracle.svm.core.annotate.TargetElement;
34+
import com.oracle.svm.core.jdk.JDK21OrEarlier;
35+
import com.oracle.svm.core.jdk.JDKInitializedAtBuildTime;
36+
import com.oracle.svm.core.jdk.JDKLatest;
37+
38+
import jdk.graal.compiler.serviceprovider.JavaVersionUtil;
39+
40+
@TargetClass(className = "jdk.xml.internal.SecuritySupport", onlyWith = JDKInitializedAtBuildTime.class)
41+
public final class Target_jdk_xml_internal_SecuritySupport {
42+
43+
@Alias //
44+
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias, isFinal = true) //
45+
static Properties cacheProps = new Properties();
46+
@Alias //
47+
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias) //
48+
private static volatile boolean firstTime = true;
49+
50+
@Substitute
51+
public static String readConfig(String propName, boolean stax) {
52+
// always load the default configuration file
53+
if (firstTime) {
54+
synchronized (cacheProps) {
55+
if (firstTime) {
56+
boolean found = XMLSupport.loadJaxpProperties();
57+
58+
// attempts to find stax.properties only if jaxp.properties is not available
59+
if (stax && !found) {
60+
XMLSupport.loadStaxProperties();
61+
}
62+
63+
// load the custom configure on top of the default if any
64+
String configFile = Target_jdk_xml_internal_SecuritySupport
65+
.getSystemProperty(JavaVersionUtil.JAVA_SPEC > 21 ? Target_jdk_xml_internal_JdkConstants.CONFIG_FILE_PROPNAME : Target_jdk_xml_internal_JdkConstants.CONFIG_FILE);
66+
if (configFile != null) {
67+
loadProperties(configFile);
68+
}
69+
70+
firstTime = false;
71+
}
72+
}
73+
}
74+
75+
return cacheProps.getProperty(propName);
76+
}
77+
78+
@Alias
79+
static native boolean loadProperties(String resourceName);
80+
81+
@Alias
82+
public static native String getSystemProperty(final String propName);
83+
}
84+
85+
@TargetClass(className = "jdk.xml.internal.JdkConstants", onlyWith = JDKInitializedAtBuildTime.class)
86+
final class Target_jdk_xml_internal_JdkConstants {
87+
@Alias //
88+
@TargetElement(onlyWith = JDKLatest.class) //
89+
public static String CONFIG_FILE_PROPNAME;
90+
91+
@Alias //
92+
@TargetElement(onlyWith = JDK21OrEarlier.class) //
93+
public static String CONFIG_FILE;
94+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core.jdk.xml;
26+
27+
import java.io.File;
28+
import java.io.IOException;
29+
import java.io.InputStream;
30+
import java.nio.file.Files;
31+
import java.nio.file.Path;
32+
import java.nio.file.Paths;
33+
34+
import org.graalvm.nativeimage.Platform;
35+
import org.graalvm.nativeimage.Platforms;
36+
import org.graalvm.nativeimage.impl.ConfigurationCondition;
37+
import org.graalvm.nativeimage.impl.RuntimeResourceSupport;
38+
39+
import com.oracle.svm.core.jdk.Resources;
40+
import com.oracle.svm.core.util.BasedOnJDKClass;
41+
import com.oracle.svm.core.util.VMError;
42+
import com.oracle.svm.util.ReflectionUtil;
43+
44+
@BasedOnJDKClass(className = "jdk.xml.internal.SecuritySupport")
45+
public final class XMLSupport {
46+
47+
private static final Module xmlModule = ReflectionUtil.lookupClass("jdk.xml.internal.SecuritySupport").getModule();
48+
private static final String JAXP_PROPERTIES = "jaxp.properties";
49+
private static final String STAX_PROPERTIES = "stax.properties";
50+
51+
static boolean loadJaxpProperties() {
52+
return loadProperties(JAXP_PROPERTIES);
53+
}
54+
55+
static void loadStaxProperties() {
56+
loadProperties(STAX_PROPERTIES);
57+
}
58+
59+
private static boolean loadProperties(String resource) {
60+
try (InputStream in = Resources.createInputStream(xmlModule, resource)) {
61+
if (in == null) {
62+
return false;
63+
}
64+
Target_jdk_xml_internal_SecuritySupport.cacheProps.load(in);
65+
return true;
66+
} catch (IOException e) {
67+
// shouldn't happen, but required by method Properties.load
68+
throw VMError.shouldNotReachHere(e);
69+
}
70+
}
71+
72+
@Platforms(Platform.HOSTED_ONLY.class)
73+
public static void readProperties() {
74+
byte[] jaxpProperty = readProperties(Paths.get(System.getProperty("java.home"), "conf", JAXP_PROPERTIES)
75+
.toAbsolutePath().normalize());
76+
byte[] staxProperty = jaxpProperty != null ? null
77+
: readProperties(Paths.get(System.getProperty("java.home"), "conf", STAX_PROPERTIES)
78+
.toAbsolutePath().normalize());
79+
addResource(xmlModule, JAXP_PROPERTIES, jaxpProperty);
80+
addResource(xmlModule, STAX_PROPERTIES, staxProperty);
81+
}
82+
83+
@Platforms(Platform.HOSTED_ONLY.class)
84+
public static void addResource(Module module, String resourcePath, byte[] resourceContent) {
85+
if (resourceContent != null) {
86+
RuntimeResourceSupport.singleton().injectResource(module, resourcePath, resourceContent, "Added via " + XMLSupport.class.getName());
87+
} else {
88+
Resources.currentLayer().registerNegativeQuery(module, resourcePath);
89+
}
90+
RuntimeResourceSupport.singleton().addCondition(ConfigurationCondition.alwaysTrue(), module, resourcePath);
91+
}
92+
93+
@Platforms(Platform.HOSTED_ONLY.class)
94+
private static byte[] readProperties(Path path) {
95+
File f = path.toFile();
96+
if (f.exists()) {
97+
try {
98+
return Files.readAllBytes(path);
99+
} catch (IOException e) {
100+
// shouldn't happen, but required by method getFileInputStream
101+
}
102+
}
103+
return null;
104+
}
105+
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JDKInitializationFeature.java

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@
2626

2727
import java.lang.reflect.Field;
2828

29-
import com.oracle.svm.core.FutureDefaultsOptions;
3029
import org.graalvm.nativeimage.ImageSingletons;
3130
import org.graalvm.nativeimage.Platform;
3231
import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport;
3332

33+
import com.oracle.svm.core.FutureDefaultsOptions;
3434
import com.oracle.svm.core.ParsingReason;
3535
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
3636
import com.oracle.svm.core.feature.InternalFeature;
@@ -53,7 +53,7 @@
5353

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

5858
@Override
5959
public void afterRegistration(AfterRegistrationAccess access) {
@@ -72,8 +72,10 @@ public void afterRegistration(AfterRegistrationAccess access) {
7272
rci.initializeAtBuildTime("java.net", JDK_CLASS_REASON);
7373
rci.initializeAtBuildTime("java.nio", JDK_CLASS_REASON);
7474
rci.initializeAtBuildTime("java.text", JDK_CLASS_REASON);
75+
7576
rci.initializeAtBuildTime("java.time", JDK_CLASS_REASON);
7677
rci.initializeAtRunTime("java.time.chrono.HijrahChronology", "Reads java.home in class initializer.");
78+
7779
rci.initializeAtBuildTime("java.util", JDK_CLASS_REASON);
7880
rci.initializeAtRunTime("java.util.concurrent.SubmissionPublisher", "Executor service must be recomputed");
7981

@@ -83,7 +85,6 @@ public void afterRegistration(AfterRegistrationAccess access) {
8385
rci.initializeAtBuildTime("javax.naming", JDK_CLASS_REASON);
8486
rci.initializeAtBuildTime("javax.net", JDK_CLASS_REASON);
8587
rci.initializeAtBuildTime("javax.tools", JDK_CLASS_REASON);
86-
rci.initializeAtBuildTime("javax.xml", JDK_CLASS_REASON);
8788

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

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

146138
/* XML-related */
147139
if (FutureDefaultsOptions.isJDKInitializedAtRunTime()) {
148-
// GR-50683 should remove this part
149-
rci.initializeAtBuildTime("com.sun.xml", JDK_CLASS_REASON);
150-
rci.initializeAtBuildTime("com.sun.org.apache", JDK_CLASS_REASON);
151-
rci.initializeAtBuildTime("com.sun.org.slf4j.internal", JDK_CLASS_REASON);
140+
/*
141+
* still initialization due to the security services that reach the XMLSecurityManager
142+
*/
143+
rci.initializeAtBuildTime("javax.xml.catalog.CatalogImpl", "Security providers initialized at build time: jdk.xml.internal.XMLSecurityManager");
144+
rci.initializeAtBuildTime("javax.xml.catalog.CatalogResolver$NotFoundAction$1", "Security providers initialized at build time: jdk.xml.internal.XMLSecurityManager");
145+
rci.initializeAtBuildTime("javax.xml.catalog.CatalogResolver$NotFoundAction$2", "Security providers initialized at build time: jdk.xml.internal.XMLSecurityManager");
146+
rci.initializeAtBuildTime("javax.xml.catalog.CatalogResolver$NotFoundAction$3", "Security providers initialized at build time: jdk.xml.internal.XMLSecurityManager");
147+
rci.initializeAtBuildTime("javax.xml.catalog.SystemSuffix", "Security providers initialized at build time: jdk.xml.internal.XMLSecurityManager");
148+
rci.initializeAtBuildTime("javax.xml.catalog.SystemEntry", "Security providers initialized at build time: jdk.xml.internal.XMLSecurityManager");
149+
rci.initializeAtBuildTime("javax.xml.catalog.CatalogFeatures", "Security providers initialized at build time: jdk.xml.internal.XMLSecurityManager");
150+
rci.initializeAtBuildTime("javax.xml.catalog.BaseEntry$CatalogEntryType", "Security providers initialized at build time: jdk.xml.internal.XMLSecurityManager");
151+
rci.initializeAtBuildTime("javax.xml.catalog.CatalogFeatures$State", "Security providers initialized at build time: jdk.xml.internal.XMLSecurityManager");
152+
rci.initializeAtBuildTime("javax.xml.catalog.PublicEntry", "Security providers initialized at build time: jdk.xml.internal.XMLSecurityManager");
152153
} else {
154+
rci.initializeAtBuildTime("jdk.xml", JDK_CLASS_REASON);
155+
/*
156+
* The XML classes have cyclic class initializer dependencies, and class initialization
157+
* can deadlock/fail when initialization is started at the "wrong part" of the cycle.
158+
* Force-initializing the correct class of the cycle here, in addition to the
159+
* "whole package" initialization above, breaks the cycle because it triggers immediate
160+
* initilalization here before the static analysis is started.
161+
*/
162+
rci.initializeAtBuildTime("jdk.xml.internal.JdkXmlUtils", JDK_CLASS_REASON);
163+
153164
rci.initializeAtBuildTime("com.sun.xml", JDK_CLASS_REASON);
154165
rci.initializeAtBuildTime("com.sun.org.apache", JDK_CLASS_REASON);
155166
rci.initializeAtBuildTime("com.sun.org.slf4j.internal", JDK_CLASS_REASON);
@@ -229,8 +240,6 @@ public void afterRegistration(AfterRegistrationAccess access) {
229240
rci.initializeAtRunTime("jdk.internal.foreign.abi.fallback.FFIABI", "Fails build-time initialization");
230241
rci.initializeAtRunTime("sun.reflect.misc.Trampoline", "Fails build-time initialization");
231242

232-
rci.initializeAtRunTime("com.sun.org.apache.xml.internal.serialize.HTMLdtd", "Fails build-time initialization");
233-
234243
rci.initializeAtRunTime("sun.security.ssl.SSLContextImpl$DefaultSSLContextHolder", "Stores secure random");
235244
rci.initializeAtRunTime("sun.security.ssl.SSLSocketFactoryImpl", "Stores secure random");
236245
rci.initializeAtRunTime("sun.security.provider.certpath.ssl.SSLServerCertStore", "Stores secure random");

0 commit comments

Comments
 (0)