com.oracle.svm.thirdparty.ICU4JFeature Maven / Gradle / Ivy
Show all versions of library-support Show documentation
/*
* Copyright (c) 2017, 2018, 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.thirdparty;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BooleanSupplier;
import org.graalvm.nativeimage.Feature;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.RuntimeReflection;
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.annotate.InjectAccessors;
import com.oracle.svm.core.annotate.RecomputeFieldValue;
import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind;
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.util.VMError;
/**
* ICU4JFeature enables ICU4J library ({@link "http://site.icu-project.org/"} to be used in SVM.
*
* The main obstacle in using the ICU4J library as is was that the library relies on class loader to
* fetch localization data from resource files included in the ICU4J jar archive. This feature is
* not supported by SVM, so the next option was to read the resource files from the file system. The
* following code addresses several issues that occurred when specifying
* com.ibm.icu.impl.ICUBinary.dataPath
system property in runtime (standard ICU4J
* feature).
*/
@AutomaticFeature
public final class ICU4JFeature implements Feature {
static final class IsEnabled implements BooleanSupplier {
@Override
public boolean getAsBoolean() {
return ImageSingletons.contains(ICU4JFeature.class);
}
}
@Override
public boolean isInConfiguration(IsInConfigurationAccess access) {
return access.findClassByName("com.ibm.icu.impl.ClassLoaderUtil") != null;
}
@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
registerShimClass(access, "com.ibm.icu.text.NumberFormatServiceShim");
registerShimClass(access, "com.ibm.icu.text.CollatorServiceShim");
}
private static void registerShimClass(BeforeAnalysisAccess access, String shimClassName) {
Class> shimClass = access.findClassByName(shimClassName);
if (shimClass != null) {
RuntimeReflection.registerForReflectiveInstantiation(shimClass);
} else {
throw VMError.shouldNotReachHere(shimClassName + " not found");
}
}
static class Helper {
/** Dummy ClassLoader used only for resource loading. */
// Checkstyle: stop
static final ClassLoader DUMMY_LOADER = new ClassLoader(null) {
};
// CheckStyle: resume
}
}
@TargetClass(className = "com.ibm.icu.impl.ClassLoaderUtil", onlyWith = ICU4JFeature.IsEnabled.class)
final class Target_com_ibm_icu_impl_ClassLoaderUtil {
@Substitute
// Checkstyle: stop
public static ClassLoader getClassLoader() {
return ICU4JFeature.Helper.DUMMY_LOADER;
}
// Checkstyle: resume
}
@TargetClass(className = "com.ibm.icu.impl.ICUBinary", onlyWith = ICU4JFeature.IsEnabled.class)
final class Target_com_ibm_icu_impl_ICUBinary {
@Alias
static native void addDataFilesFromPath(String dataPath, List> files);
@Alias @InjectAccessors(IcuDataFilesAccessors.class) static List> icuDataFiles;
static final class IcuDataFilesAccessors {
private static final String ICU4J_DATA_PATH_SYS_PROP = "com.ibm.icu.impl.ICUBinary.dataPath";
private static final String ICU4J_DATA_PATH_ENV_VAR = "ICU4J_DATA_PATH";
private static final String NO_DATA_PATH_ERR_MSG = "No ICU4J data path was set or found. This will likely end up with a MissingResourceException. " +
"To take advantage of the ICU4J library, you should either set system property, " +
ICU4J_DATA_PATH_SYS_PROP +
", or set environment variable, " +
ICU4J_DATA_PATH_ENV_VAR +
", to contain path to your ICU4J icudt directory";
private static volatile List> instance;
static List> get() {
if (instance == null) {
// Checkstyle: allow synchronization
synchronized (IcuDataFilesAccessors.class) {
if (instance == null) {
instance = new ArrayList<>();
String dataPath = System.getProperty(ICU4J_DATA_PATH_SYS_PROP);
if (dataPath == null || dataPath.isEmpty()) {
dataPath = System.getenv(ICU4J_DATA_PATH_ENV_VAR);
}
if (dataPath != null && !dataPath.isEmpty()) {
addDataFilesFromPath(dataPath, instance);
} else {
System.err.println(NO_DATA_PATH_ERR_MSG);
}
}
}
// Checkstyle: disallow synchronization
}
return instance;
}
}
}
@TargetClass(className = "com.ibm.icu.impl.ICUResourceBundle", onlyWith = ICU4JFeature.IsEnabled.class)
final class Target_com_ibm_icu_impl_ICUResourceBundle {
@Alias @RecomputeFieldValue(kind = Kind.FromAlias, isFinal = true)
// Checkstyle: stop
private static final ClassLoader ICU_DATA_CLASS_LOADER = ICU4JFeature.Helper.DUMMY_LOADER;
// Checkstyle: resume
@SuppressWarnings("unused")
@Substitute
// Checkstyle: stop
private static void addBundleBaseNamesFromClassLoader(final String bn, final ClassLoader root, final Set names) {
}
// Checkstyle: resume
}
@TargetClass(className = "com.ibm.icu.impl.ICUResourceBundle$WholeBundle", onlyWith = ICU4JFeature.IsEnabled.class)
final class Target_com_ibm_icu_impl_ICUResourceBundle_WholeBundle {
@Alias @RecomputeFieldValue(kind = Kind.Reset)
// Checkstyle: stop
ClassLoader loader;
// Checkstyle: resume
}
@TargetClass(className = "com.ibm.icu.impl.SoftCache", onlyWith = ICU4JFeature.IsEnabled.class)
final class Target_com_ibm_icu_impl_SoftCache {
// Checkstyle: stop
@Alias @RecomputeFieldValue(kind = Kind.NewInstance, declClass = ConcurrentHashMap.class) private ConcurrentHashMap, ?> map;
// Checkstyle: resume
}
@TargetClass(className = "com.ibm.icu.impl.ICUResourceBundle$AvailEntry", onlyWith = ICU4JFeature.IsEnabled.class)
final class Target_com_ibm_icu_impl_ICUResourceBundle_AvailEntry {
@Alias @RecomputeFieldValue(kind = Kind.Reset)
// Checkstyle: stop
ClassLoader loader;
// Checkstyle: resume
}
@TargetClass(className = "com.ibm.icu.util.TimeZone", onlyWith = ICU4JFeature.IsEnabled.class)
final class Target_com_ibm_icu_util_TimeZone {
// clear default time zone to force reinitialization at run time
@Alias @RecomputeFieldValue(kind = Kind.Reset) private static Target_com_ibm_icu_util_TimeZone defaultZone;
}