8187987: Add a mechanism to configure custom variants in HijrahChronology

Reviewed-by: joehw, rriggs
This commit is contained in:
Naoto Sato 2020-01-21 08:02:10 -08:00
parent f129cc4328
commit 0ae7207e95
8 changed files with 266 additions and 12 deletions

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2014, 2020, 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
@ -39,7 +39,7 @@ $(eval $(call IncludeCustomExtension, CompileJavaModules.gmk))
# Module specific build settings
java.base_ADD_JAVAC_FLAGS += -Xdoclint:all/protected,-reference,-accessibility '-Xdoclint/package:java.*,javax.*' -XDstringConcat=inline
java.base_COPY += .icu .dat .spp .nrm content-types.properties hijrah-config-islamic-umalqura.properties
java.base_COPY += .icu .dat .spp .nrm content-types.properties hijrah-config-Hijrah-umalqura_islamic-umalqura.properties
java.base_CLEAN += intrinsic.properties
java.base_EXCLUDE_FILES += \

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2020, 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
@ -60,10 +60,15 @@ package java.time.chrono;
import static java.time.temporal.ChronoField.EPOCH_DAY;
import java.io.FilePermission;
import java.io.IOException;
import java.io.InputStream;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.time.Clock;
@ -189,6 +194,11 @@ import sun.util.logging.PlatformLogger;
* </tr>
* </tbody>
* </table>
* <p>
* Additional variants may be added by providing configuration properties files in
* {@code <JAVA_HOME>/conf/chronology} directory. The properties
* files should follow the naming convention of
* {@code hijrah-config-<chronology id>_<calendar type>.properties}.
*
* @since 1.8
*/
@ -278,6 +288,11 @@ public final class HijrahChronology extends AbstractChronology implements Serial
// Register it by its aliases
AbstractChronology.registerChrono(INSTANCE, "Hijrah");
AbstractChronology.registerChrono(INSTANCE, "islamic");
// custom config chronologies
CONF_PATH = Path.of(AccessController.doPrivileged((PrivilegedAction<String>)
() -> System.getProperty("java.home")), "conf", "chronology");
registerCustomChrono();
}
/**
@ -800,27 +815,40 @@ public final class HijrahChronology extends AbstractChronology implements Serial
private static final String KEY_TYPE = "type";
private static final String KEY_VERSION = "version";
private static final String KEY_ISO_START = "iso-start";
private static final Path CONF_PATH;
/**
* Return the configuration properties from the resource.
* <p>
* The location of the variant configuration resource is:
* <pre>
* "/java/time/chrono/hijrah-config-" + calendarType + ".properties"
* "/java/time/chrono/" (for "islamic-umalqura" type), or
* "<JAVA_HOME>/conf/chronology/" +
* "hijrah-config-" + chronologyId + "_" + calendarType + ".properties"
* </pre>
*
* @param chronologyId the chronology ID of the calendar variant
* @param calendarType the calendarType of the calendar variant
* @return a Properties containing the properties read from the resource.
* @throws Exception if access to the property resource fails
*/
private Properties readConfigProperties(final String calendarType) throws Exception {
String resourceName = RESOURCE_PREFIX + calendarType + RESOURCE_SUFFIX;
PrivilegedAction<InputStream> getResourceAction = () -> HijrahChronology.class.getResourceAsStream(resourceName);
private static Properties readConfigProperties(final String chronologyId, final String calendarType) throws Exception {
String resourceName = RESOURCE_PREFIX + chronologyId + "_" + calendarType + RESOURCE_SUFFIX;
PrivilegedAction<InputStream> getResourceAction = calendarType.equals("islamic-umalqura") ?
() -> HijrahChronology.class.getResourceAsStream(resourceName) :
() -> {
try {
return Files.newInputStream(CONF_PATH.resolve(resourceName),
StandardOpenOption.READ);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
};
FilePermission perm1 = new FilePermission("<<ALL FILES>>", "read");
RuntimePermission perm2 = new RuntimePermission("accessSystemModules");
try (InputStream is = AccessController.doPrivileged(getResourceAction, null, perm1, perm2)) {
if (is == null) {
throw new RuntimeException("Hijrah calendar resource not found: /java/time/chrono/" + resourceName);
throw new RuntimeException("Hijrah calendar resource not found: " + resourceName);
}
Properties props = new Properties();
props.load(is);
@ -841,7 +869,7 @@ public final class HijrahChronology extends AbstractChronology implements Serial
*/
private void loadCalendarData() {
try {
Properties props = readConfigProperties(calendarType);
Properties props = readConfigProperties(typeId, calendarType);
Map<Integer, int[]> years = new HashMap<>();
int minYear = Integer.MAX_VALUE;
@ -1009,6 +1037,44 @@ public final class HijrahChronology extends AbstractChronology implements Serial
}
}
/**
* Look for Hijrah chronology variant properties files in
* <JAVA_HOME>/conf/chronology directory. Then register its chronology, if any.
*/
private static void registerCustomChrono() {
AccessController.doPrivileged(
(PrivilegedAction<Void>)() -> {
if (Files.isDirectory(CONF_PATH)) {
try {
Files.list(CONF_PATH)
.map(p -> p.getFileName().toString())
.filter(fn -> fn.matches("hijrah-config-[^\\.]+\\.properties"))
.map(fn -> fn.replaceAll("(hijrah-config-|\\.properties)", ""))
.forEach(idtype -> {
int delimiterPos = idtype.indexOf('_');
// '_' should be somewhere in the middle of idtype
if (delimiterPos > 1 && delimiterPos < idtype.length() - 1) {
AbstractChronology.registerChrono(
new HijrahChronology(
idtype.substring(0, delimiterPos),
idtype.substring(delimiterPos + 1)));
} else {
PlatformLogger.getLogger("java.time.chrono")
.warning("Hijrah custom config init failed." +
"'<id>_<type>' name convention not followed: " + idtype);
}
});
} catch (IOException e) {
PlatformLogger.getLogger("java.time.chrono")
.warning("Hijrah custom config init failed.", e);
}
}
return null;
},
null,
new FilePermission("<<ALL FILES>>", "read"));
}
//-----------------------------------------------------------------------
/**
* Writes the Chronology using a

View File

@ -1,4 +1,4 @@
# Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2013, 2020, 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

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2020, 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.
*
* 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.
*/
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.time.chrono.ChronoLocalDateTime;
import java.time.chrono.Chronology;
import java.util.Locale;
public class HijrahConfigCheck {
private static final String CALTYPE = "islamic-test";
public static void main(String... args) {
// Availability test
if (Chronology.getAvailableChronologies().stream()
.filter(c -> c.getCalendarType().equals(CALTYPE))
.count() != 1) {
throw new RuntimeException(CALTYPE + " chronology was not found, or " +
"appeared more than once in Chronology.getAvailableChronologies()");
}
// Instantiation tests
Chronology c1 = Chronology.of(CALTYPE);
Chronology c2 = Chronology.ofLocale(Locale.forLanguageTag("und-u-ca-" + CALTYPE ));
if (!c1.equals(c2)) {
throw new RuntimeException(CALTYPE + " chronologies differ. c1: " + c1 +
", c2: " + c2);
}
// Date test
// 2020-01-10 is AH 1000-01-10 in islamic-test config
LocalDateTime iso = LocalDateTime.of(LocalDate.of(2020, 1, 10), LocalTime.MIN);
ChronoLocalDateTime hijrah = c1.date(1000, 1, 10).atTime(LocalTime.MIN);
if (!iso.toInstant(ZoneOffset.UTC).equals(hijrah.toInstant(ZoneOffset.UTC))) {
throw new RuntimeException("test Hijrah date is incorrect. LocalDate: " +
iso + ", test date: " + hijrah);
}
}
}

View File

@ -0,0 +1,78 @@
/*
* Copyright (c) 2020, 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.
*
* 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.
*/
import java.nio.file.Files;
import java.nio.file.Path;
import tests.Helper;
import tests.JImageGenerator;
/*
* @test
* @summary Tests whether a custom Hijrah configuration properties file works correctly
* @bug 8187987
* @requires (vm.compMode != "Xcomp" & os.maxMemory >= 2g)
* @library /tools/lib
* @modules java.base/jdk.internal.jimage
* jdk.jdeps/com.sun.tools.classfile
* jdk.jlink/jdk.tools.jimage
* jdk.compiler
* @build HijrahConfigCheck tests.*
* @run main/othervm -Xmx1g HijrahConfigTest
*/
public class HijrahConfigTest {
private static final String TEST_CONFIG = "hijrah-config-Hijrah-test_islamic-test.properties";
public static void main(String[] args) throws Exception {
Helper helper = Helper.newHelper();
if (helper == null) {
System.err.println("Test not run");
return;
}
// Create the test JDK image
Path outputPath = helper.createNewImageDir("HijrahConfigTest");
JImageGenerator.getJLinkTask()
.output(outputPath)
.addMods("java.base")
.call().assertSuccess();
// Install the test hijrah configuration properties
Path confPath = outputPath.resolve("conf").resolve("chronology");
Files.createDirectory(confPath);
Files.copy(Path.of(System.getProperty("test.src"), TEST_CONFIG),
confPath.resolve(TEST_CONFIG));
// Run tests
Path launcher = outputPath.resolve("bin").resolve("java");
ProcessBuilder builder = new ProcessBuilder(
launcher.toAbsolutePath().toString(), "-ea", "-esa", "HijrahConfigCheck");
Process p = builder.inheritIO().start();
p.waitFor();
int exitValue = p.exitValue();
if (exitValue != 0) {
throw new RuntimeException("HijrahConfigTest failed. Exit value: " + exitValue);
}
}
}

View File

@ -0,0 +1,49 @@
# Copyright (c) 2020, 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.
#
# 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.
# A test configuration properties file for a custom Hijrah chronology.
# The data here are solely hypothetical and do not represent any of
# the actual Hijrah variants.
# Version of this definition
version=0.1
# Java chronology ID
id=Hijrah-test
# Standard calendar type specification
type=islamic-test
# defines the corresponding ISO date to the earliest Hijrah date
iso-start=2020-01-01
# 1 2 3 4 5 6 7 8 9 10 11 12
1000=29 30 29 29 30 29 30 29 30 30 30 29
1001=30 29 30 29 29 30 29 29 30 30 30 30
1002=29 30 29 30 29 29 29 30 29 30 30 30
1003=29 30 30 29 30 29 29 29 30 29 30 30
1004=29 30 30 29 30 29 30 29 29 30 29 30
1005=30 29 30 29 30 30 29 30 29 30 29 30
1006=29 29 30 29 30 30 29 30 29 30 30 29
1007=30 29 29 30 29 30 29 30 30 29 30 30
1008=29 30 29 29 30 29 29 30 30 30 29 30
1009=30 29 30 29 29 30 29 29 30 30 29 30
1010=30 30 29 30 29 29 30 29 29 30 30 29

View File

@ -1,5 +1,5 @@
# java.time tests use TestNG
TestNG.dirs = ..
othervm.dirs = java/time/chrono
lib.dirs = /test/lib
lib.dirs = /test/lib /test/jdk/tools/lib
lib.build = jdk.test.lib.RandomFactory

View File

@ -1,5 +1,5 @@
# java.time tests use TestNG
TestNG.dirs = ..
othervm.dirs = java/time/chrono java/time/format
lib.dirs = /test/lib
lib.dirs = /test/lib /test/jdk/tools/lib
lib.build = jdk.test.lib.RandomFactory