diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtil.java b/dubbo-common/src/main/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtil.java index 0149cc9ee7c3..2ff1165811f0 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtil.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtil.java @@ -30,6 +30,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.IdentityHashMap; +import java.util.Locale; import java.util.Map; public final class JavaBeanSerializeUtil { @@ -306,7 +307,7 @@ private static void deserializeInternal( } private static Method getSetterMethod(Class cls, String property, Class valueCls) { - String name = "set" + property.substring(0, 1).toUpperCase() + property.substring(1); + String name = "set" + property.substring(0, 1).toUpperCase(Locale.ROOT) + property.substring(1); Method method = null; try { method = cls.getMethod(name, valueCls); diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java index f95a9f19abf2..0b5925e9c734 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/extension/ExtensionLoader.java @@ -66,6 +66,7 @@ import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Properties; @@ -750,7 +751,7 @@ private IllegalStateException findException(String name) { int i = 1; for (Map.Entry entry : exceptions.entrySet()) { - if (entry.getKey().toLowerCase().startsWith(name.toLowerCase())) { + if (entry.getKey().toLowerCase(Locale.ROOT).startsWith(name.toLowerCase(Locale.ROOT))) { if (i == 1) { buf.append(", possible causes: "); } @@ -922,7 +923,7 @@ private void initExtension(T instance) { */ private String getSetterProperty(Method method) { return method.getName().length() > 3 - ? method.getName().substring(3, 4).toLowerCase() + ? method.getName().substring(3, 4).toLowerCase(Locale.ROOT) + method.getName().substring(4) : ""; } @@ -1428,7 +1429,7 @@ private String findAnnotationName(Class clazz) { if (name.endsWith(type.getSimpleName())) { name = name.substring(0, name.length() - type.getSimpleName().length()); } - return name.toLowerCase(); + return name.toLowerCase(Locale.ROOT); } @SuppressWarnings("unchecked") diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/infra/support/EnvironmentAdapter.java b/dubbo-common/src/main/java/org/apache/dubbo/common/infra/support/EnvironmentAdapter.java index 0ef472af614b..7e74016b5ec2 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/infra/support/EnvironmentAdapter.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/infra/support/EnvironmentAdapter.java @@ -24,6 +24,7 @@ import org.apache.dubbo.rpc.model.ScopeModelAware; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import static org.apache.dubbo.common.constants.CommonConstants.COMMA_SPLIT_PATTERN; @@ -70,7 +71,7 @@ public Map getExtraAttributes(Map params) { String value = ConfigurationUtils.getProperty(applicationModel, key); if (value != null) { // since 3.2 - parameters.put(key.toLowerCase(), value); + parameters.put(key.toLowerCase(Locale.ROOT), value); // upper-case key kept for compatibility parameters.put(key, value); } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodUtils.java index a29d93f1eb4b..7d1de538d946 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/MethodUtils.java @@ -27,6 +27,7 @@ import java.util.Arrays; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import java.util.Objects; import java.util.function.Predicate; @@ -429,7 +430,7 @@ static String extractFieldName(Method method) { } if (StringUtils.isNotEmpty(fieldName)) { - fieldName = fieldName.substring(0, 1).toLowerCase() + fieldName.substring(1); + fieldName = fieldName.substring(0, 1).toLowerCase(Locale.ROOT) + fieldName.substring(1); } return fieldName; diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java index e861c3f2987d..1e191459a978 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/PojoUtils.java @@ -48,6 +48,7 @@ import java.util.IdentityHashMap; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -267,11 +268,11 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl String methodName = method.getName(); Object value = null; if (methodName.length() > 3 && methodName.startsWith("get")) { - value = map.get(methodName.substring(3, 4).toLowerCase() + methodName.substring(4)); + value = map.get(methodName.substring(3, 4).toLowerCase(Locale.ROOT) + methodName.substring(4)); } else if (methodName.length() > 2 && methodName.startsWith("is")) { - value = map.get(methodName.substring(2, 3).toLowerCase() + methodName.substring(3)); + value = map.get(methodName.substring(2, 3).toLowerCase(Locale.ROOT) + methodName.substring(3)); } else { - value = map.get(methodName.substring(0, 1).toLowerCase() + methodName.substring(1)); + value = map.get(methodName.substring(0, 1).toLowerCase(Locale.ROOT) + methodName.substring(1)); } if (value instanceof Map && !Map.class.isAssignableFrom(method.getReturnType())) { value = realize0(value, method.getReturnType(), null, new IdentityHashMap<>()); @@ -733,7 +734,7 @@ private static Object getDefaultValue(Class parameterType) { } private static Method getSetterMethod(Class cls, String property, Class valueCls) { - String name = "set" + property.substring(0, 1).toUpperCase() + property.substring(1); + String name = "set" + property.substring(0, 1).toUpperCase(Locale.ROOT) + property.substring(1); Method method = NAME_METHODS_CACHE.get(cls.getName() + "." + name + "(" + valueCls.getName() + ")"); if (method == null) { try { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java index 3ab2a9d34284..9962c9c37a44 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/ReflectUtils.java @@ -39,6 +39,7 @@ import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -1102,11 +1103,11 @@ public static boolean isBeanPropertyReadMethod(Method method) { public static String getPropertyNameFromBeanReadMethod(Method method) { if (isBeanPropertyReadMethod(method)) { if (method.getName().startsWith("get")) { - return method.getName().substring(3, 4).toLowerCase() + return method.getName().substring(3, 4).toLowerCase(Locale.ROOT) + method.getName().substring(4); } if (method.getName().startsWith("is")) { - return method.getName().substring(2, 3).toLowerCase() + return method.getName().substring(2, 3).toLowerCase(Locale.ROOT) + method.getName().substring(3); } } @@ -1125,7 +1126,7 @@ public static boolean isBeanPropertyWriteMethod(Method method) { public static String getPropertyNameFromBeanWriteMethod(Method method) { if (isBeanPropertyWriteMethod(method)) { - return method.getName().substring(3, 4).toLowerCase() + return method.getName().substring(3, 4).toLowerCase(Locale.ROOT) + method.getName().substring(4); } return null; diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java index d140ed4454ef..08c7e5c9ccf7 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/common/utils/StringUtils.java @@ -28,6 +28,7 @@ import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TreeMap; @@ -944,7 +945,7 @@ public static String camelToSplitName(String camelName, String split) { if (!isWord(camelName)) { // convert Ab-Cd-Ef to ab-cd-ef if (isSplitCase(camelName, split.charAt(0))) { - return camelName.toLowerCase(); + return camelName.toLowerCase(Locale.ROOT); } // not camel case return camelName; @@ -968,7 +969,7 @@ public static String camelToSplitName(String camelName, String split) { buf.append(ch); } } - return buf == null ? camelName.toLowerCase() : buf.toString().toLowerCase(); + return buf == null ? camelName.toLowerCase(Locale.ROOT) : buf.toString().toLowerCase(Locale.ROOT); } private static boolean isSplitCase(String str, char separator) { @@ -1002,7 +1003,7 @@ private static boolean isWord(char ch) { * @return */ public static String snakeToSplitName(String snakeName, String split) { - String lowerCase = snakeName.toLowerCase(); + String lowerCase = snakeName.toLowerCase(Locale.ROOT); if (isSnakeCase(snakeName)) { return replace(lowerCase, "_", split); } @@ -1010,7 +1011,9 @@ public static String snakeToSplitName(String snakeName, String split) { } protected static boolean isSnakeCase(String str) { - return str.contains("_") || str.equals(str.toLowerCase()) || str.equals(str.toUpperCase()); + return str.contains("_") + || str.equals(str.toLowerCase(Locale.ROOT)) + || str.equals(str.toUpperCase(Locale.ROOT)); } /** @@ -1053,11 +1056,11 @@ public static String trim(String str) { } public static String toURLKey(String key) { - return key.toLowerCase().replaceAll(SEPARATOR_REGEX, HIDE_KEY_PREFIX); + return key.toLowerCase(Locale.ROOT).replaceAll(SEPARATOR_REGEX, HIDE_KEY_PREFIX); } public static String toOSStyleKey(String key) { - key = key.toUpperCase().replaceAll(DOT_REGEX, UNDERLINE_SEPARATOR); + key = key.toUpperCase(Locale.ROOT).replaceAll(DOT_REGEX, UNDERLINE_SEPARATOR); if (!key.startsWith("DUBBO_")) { key = "DUBBO_" + key; } diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java index 35da43e059ee..98b59ba473d7 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/AbstractConfig.java @@ -59,6 +59,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -272,26 +273,27 @@ private static void appendParameters0( protected static String extractPropertyName(String setter) { String propertyName = setter.substring("set".length()); - propertyName = propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1); + propertyName = propertyName.substring(0, 1).toLowerCase(Locale.ROOT) + propertyName.substring(1); return propertyName; } private static String calculatePropertyToGetter(String name) { - return "get" + name.substring(0, 1).toUpperCase() + name.substring(1); + return "get" + name.substring(0, 1).toUpperCase(Locale.ROOT) + name.substring(1); } private static String calculatePropertyToSetter(String name) { - return "set" + name.substring(0, 1).toUpperCase() + name.substring(1); + return "set" + name.substring(0, 1).toUpperCase(Locale.ROOT) + name.substring(1); } private static String calculatePropertyFromGetter(String name) { int i = name.startsWith("get") ? 3 : 2; - return StringUtils.camelToSplitName(name.substring(i, i + 1).toLowerCase() + name.substring(i + 1), "."); + return StringUtils.camelToSplitName( + name.substring(i, i + 1).toLowerCase(Locale.ROOT) + name.substring(i + 1), "."); } private static String calculateAttributeFromGetter(String getter) { int i = getter.startsWith("get") ? 3 : 2; - return getter.substring(i, i + 1).toLowerCase() + getter.substring(i + 1); + return getter.substring(i, i + 1).toLowerCase(Locale.ROOT) + getter.substring(i + 1); } private static void invokeSetParameters(Class c, Object o, Map map) { @@ -979,7 +981,8 @@ private void invokeSetParameters(Map values, Object obj) { private boolean isIgnoredAttribute(Class clazz, String propertyName) { Method getter = null; - String capitalizePropertyName = propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); + String capitalizePropertyName = + propertyName.substring(0, 1).toUpperCase(Locale.ROOT) + propertyName.substring(1); try { getter = clazz.getMethod("get" + capitalizePropertyName); } catch (NoSuchMethodException e) { diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/context/AbstractConfigManager.java b/dubbo-common/src/main/java/org/apache/dubbo/config/context/AbstractConfigManager.java index 8c2acd809205..d23104524b1b 100644 --- a/dubbo-common/src/main/java/org/apache/dubbo/config/context/AbstractConfigManager.java +++ b/dubbo-common/src/main/java/org/apache/dubbo/config/context/AbstractConfigManager.java @@ -50,6 +50,7 @@ import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -121,7 +122,7 @@ public void initialize() throws IllegalStateException { String configModeStr = (String) configuration.getProperty(ConfigKeys.DUBBO_CONFIG_MODE); try { if (StringUtils.hasText(configModeStr)) { - this.configMode = ConfigMode.valueOf(configModeStr.toUpperCase()); + this.configMode = ConfigMode.valueOf(configModeStr.toUpperCase(Locale.ROOT)); } } catch (Exception e) { String msg = "Illegal '" + ConfigKeys.DUBBO_CONFIG_MODE + "' config value [" + configModeStr diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtilTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtilTest.java index 602d7603fd05..7e4f89596e63 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtilTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtilTest.java @@ -28,6 +28,7 @@ import java.util.Collection; import java.util.Date; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.UUID; @@ -56,6 +57,23 @@ void testSerialize_Primitive_NUll() { Assertions.assertNull(descriptor); } + // In the Turkish locale "i".toUpperCase() is the dotted "İ", so "setİnfoProfile" was looked up. + @Test + void testDeserializeBeanUnderTurkishLocale() { + PersonInfo personInfo = new PersonInfo(); + personInfo.setName("dubbo"); + JavaBeanDescriptor descriptor = new JavaBeanDescriptor(BigPerson.class.getName(), JavaBeanDescriptor.TYPE_BEAN); + descriptor.setProperty("infoProfile", personInfo); + Locale defaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.forLanguageTag("tr-TR")); + try { + BigPerson person = (BigPerson) JavaBeanSerializeUtil.deserialize(descriptor); + Assertions.assertSame(personInfo, person.getInfoProfile()); + } finally { + Locale.setDefault(defaultLocale); + } + } + @Test void testDeserialize_Primitive() { JavaBeanDescriptor descriptor = new JavaBeanDescriptor(long.class.getName(), JavaBeanDescriptor.TYPE_PRIMITIVE); diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/MethodUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/MethodUtilsTest.java index 702306d3ae18..c2eb24c2b508 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/MethodUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/MethodUtilsTest.java @@ -16,7 +16,10 @@ */ package org.apache.dubbo.common.utils; +import org.apache.dubbo.rpc.model.person.BigPerson; + import java.lang.reflect.Method; +import java.util.Locale; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -118,6 +121,19 @@ void testExtractFieldName() throws Exception { Assertions.assertEquals("fieldName3", MethodUtils.extractFieldName(m7)); } + // In the Turkish locale "I".toLowerCase() is the dotless "ı". + @Test + void testExtractFieldNameUnderTurkishLocale() throws Exception { + Locale defaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.forLanguageTag("tr-TR")); + try { + Method getter = BigPerson.class.getMethod("getInfoProfile"); + Assertions.assertEquals("infoProfile", MethodUtils.extractFieldName(getter)); + } finally { + Locale.setDefault(defaultLocale); + } + } + public class MethodFieldTestClazz { public String is() { return ""; diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java index 5cb48a25a77c..c17c7739c745 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/PojoUtilsTest.java @@ -45,6 +45,7 @@ import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.UUID; @@ -296,6 +297,23 @@ void testMapToInterface() throws Exception { assertTrue(message.isUrgent()); } + // In the Turkish locale "I".toLowerCase() is the dotless "ı", so getId() looked up "ıd" in the map. + @Test + void testMapToInterfaceUnderTurkishLocale() { + Map map = new HashMap<>(); + map.put("id", "T-1"); + map.put("important", true); + Locale defaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.forLanguageTag("tr-TR")); + try { + Ticket ticket = (Ticket) PojoUtils.realize(map, Ticket.class); + assertThat(ticket.getId(), equalTo("T-1")); + assertTrue(ticket.isImportant()); + } finally { + Locale.setDefault(defaultLocale); + } + } + @Test void testJsonObjectToMap() throws Exception { Method method = PojoUtilsTest.class.getMethod("setMap", Map.class); @@ -1286,4 +1304,10 @@ interface Message { boolean isUrgent(); } + + interface Ticket { + String getId(); + + boolean isImportant(); + } } diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ReflectUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ReflectUtilsTest.java index c987b81c30bb..20983cbb401a 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ReflectUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/ReflectUtilsTest.java @@ -16,6 +16,9 @@ */ package org.apache.dubbo.common.utils; +import org.apache.dubbo.rpc.model.person.BigPerson; +import org.apache.dubbo.rpc.model.person.PersonInfo; + import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -24,6 +27,7 @@ import java.util.Collection; import java.util.Date; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -257,6 +261,21 @@ void testGetPropertyNameFromBeanWriteMethod() throws Exception { assertEquals("property", ReflectUtils.getPropertyNameFromBeanWriteMethod(method)); } + // In the Turkish locale "I".toLowerCase() is the dotless "ı". + @Test + void testGetPropertyNameFromBeanMethodUnderTurkishLocale() throws Exception { + Locale defaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.forLanguageTag("tr-TR")); + try { + Method getter = BigPerson.class.getMethod("getInfoProfile"); + assertEquals("infoProfile", ReflectUtils.getPropertyNameFromBeanReadMethod(getter)); + Method setter = BigPerson.class.getMethod("setInfoProfile", PersonInfo.class); + assertEquals("infoProfile", ReflectUtils.getPropertyNameFromBeanWriteMethod(setter)); + } finally { + Locale.setDefault(defaultLocale); + } + } + @Test void testIsPublicInstanceField() throws Exception { Field field = EmptyClass.class.getDeclaredField("set"); diff --git a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/StringUtilsTest.java b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/StringUtilsTest.java index b8250019dea5..7082b83c59d1 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/common/utils/StringUtilsTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/common/utils/StringUtilsTest.java @@ -21,6 +21,7 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -408,6 +409,22 @@ void testToOSStyleKey() { assertEquals("DUBBO_TAG1", StringUtils.toOSStyleKey("tag1")); } + // In the Turkish locale "I".toLowerCase() is the dotless "ı" and "i".toUpperCase() is the dotted "İ". + @Test + void testSplitNamesAndKeysUnderTurkishLocale() { + Locale defaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.forLanguageTag("tr-TR")); + try { + assertEquals("io-threads", StringUtils.camelToSplitName("IO-THREADS", "-")); + assertEquals("io-threads", StringUtils.snakeToSplitName("IO_THREADS", "-")); + assertEquals("iothreads", StringUtils.convertToSplitName("IOTHREADS", "-")); + assertEquals("dubbo.protocol.iothreads", StringUtils.toURLKey("DUBBO_PROTOCOL_IOTHREADS")); + assertEquals("DUBBO_PROTOCOL_IOTHREADS", StringUtils.toOSStyleKey("dubbo.protocol.iothreads")); + } finally { + Locale.setDefault(defaultLocale); + } + } + @Test void testParseParameters() { String legalStr = "[{key1:value1},{key2:value2}]"; diff --git a/dubbo-common/src/test/java/org/apache/dubbo/config/context/ConfigManagerTest.java b/dubbo-common/src/test/java/org/apache/dubbo/config/context/ConfigManagerTest.java index 74121b9b4923..97360e833eab 100644 --- a/dubbo-common/src/test/java/org/apache/dubbo/config/context/ConfigManagerTest.java +++ b/dubbo-common/src/test/java/org/apache/dubbo/config/context/ConfigManagerTest.java @@ -31,6 +31,7 @@ import org.apache.dubbo.rpc.model.FrameworkModel; import java.util.Collection; +import java.util.Locale; import java.util.Optional; import org.junit.jupiter.api.Assertions; @@ -376,6 +377,23 @@ void testConfigMode() { } } + // In the Turkish locale "override".toUpperCase() is "OVERRİDE", which is not a ConfigMode constant. + @Test + void testConfigModeUnderTurkishLocale() { + Locale defaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.forLanguageTag("tr-TR")); + try { + System.setProperty(DUBBO_CONFIG_MODE, "override"); + ApplicationModel.reset(); + ConfigManager configManager = ApplicationModel.defaultModel().getApplicationConfigManager(); + Assertions.assertEquals(ConfigMode.OVERRIDE, configManager.getConfigMode()); + } finally { + Locale.setDefault(defaultLocale); + System.clearProperty(DUBBO_CONFIG_MODE); + ApplicationModel.reset(); + } + } + @Test void testGetConfigByIdOrName() { RegistryConfig registryConfig = new RegistryConfig(); diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java index 262d84e09154..b7eb0eb52311 100644 --- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java +++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java @@ -65,6 +65,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.TreeSet; @@ -1170,7 +1171,7 @@ private static Integer parsePort(String configPort) { } private static String getValueFromConfig(ProtocolConfig protocolConfig, String key) { - String protocolPrefix = protocolConfig.getName().toUpperCase() + "_"; + String protocolPrefix = protocolConfig.getName().toUpperCase(Locale.ROOT) + "_"; String value = ConfigUtils.getSystemProperty(protocolPrefix + key); if (StringUtils.isEmpty(value)) { value = ConfigUtils.getSystemProperty(key); @@ -1179,12 +1180,12 @@ private static String getValueFromConfig(ProtocolConfig protocolConfig, String k } private static Integer getRandomPort(String protocol) { - protocol = protocol.toLowerCase(); + protocol = protocol.toLowerCase(Locale.ROOT); return RANDOM_PORT_MAP.getOrDefault(protocol, Integer.MIN_VALUE); } private static void putRandomPort(String protocol, Integer port) { - protocol = protocol.toLowerCase(); + protocol = protocol.toLowerCase(Locale.ROOT); if (!RANDOM_PORT_MAP.containsKey(protocol)) { RANDOM_PORT_MAP.put(protocol, port); logger.warn( diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java index db8944b51592..c1b98ddf0f15 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractConfigTest.java @@ -35,6 +35,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Properties; @@ -572,6 +573,39 @@ void testRefreshParametersWithOverrideConfigMode() { } } + // In the Turkish locale "I".toLowerCase() is the dotless "ı", which turned + // the property behind getIothreads()/setIothreads() into "ıothreads". + @Test + void testAppendParametersUnderTurkishLocale() { + Locale defaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.forLanguageTag("tr-TR")); + try { + ProtocolConfig protocolConfig = new ProtocolConfig(); + protocolConfig.setIothreads(8); + + Map parameters = new HashMap<>(); + AbstractConfig.appendParameters(parameters, protocolConfig); + Assertions.assertEquals("8", parameters.get("iothreads"), parameters.toString()); + } finally { + Locale.setDefault(defaultLocale); + } + } + + @Test + void testRefreshUnderTurkishLocale() { + Locale defaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.forLanguageTag("tr-TR")); + try { + SysProps.setProperty("dubbo.protocol.iothreads", "16"); + ProtocolConfig protocolConfig = new ProtocolConfig(); + protocolConfig.refresh(); + Assertions.assertEquals(16, protocolConfig.getIothreads()); + } finally { + Locale.setDefault(defaultLocale); + ApplicationModel.defaultModel().modelEnvironment().destroy(); + } + } + @Test void testOnlyPrefixedKeyTakeEffect() { try { diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java index a285c8c073f3..c081c5cfeb67 100644 --- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java +++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/ServiceConfigTest.java @@ -37,7 +37,9 @@ import org.apache.dubbo.rpc.model.FrameworkModel; import org.apache.dubbo.rpc.service.GenericService; +import java.lang.reflect.Method; import java.util.Collections; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -62,6 +64,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER; import static org.apache.dubbo.common.constants.CommonConstants.SHUTDOWN_WAIT_KEY; import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY; +import static org.apache.dubbo.config.Constants.DUBBO_PORT_TO_BIND; import static org.apache.dubbo.config.Constants.SHUTDOWN_TIMEOUT_KEY; import static org.apache.dubbo.remoting.Constants.BIND_IP_KEY; import static org.apache.dubbo.remoting.Constants.BIND_PORT_KEY; @@ -766,6 +769,22 @@ public Set removeCachedMapping(String serviceKey) { scheduledExecutorService.shutdown(); } + // In the Turkish locale "tri".toUpperCase() is "TRİ", so TRI_DUBBO_PORT_TO_BIND was never looked up. + @Test + void testGetValueFromConfigUnderTurkishLocale() throws Exception { + Method getValueFromConfig = + ServiceConfig.class.getDeclaredMethod("getValueFromConfig", ProtocolConfig.class, String.class); + getValueFromConfig.setAccessible(true); + Locale defaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.forLanguageTag("tr-TR")); + try { + SysProps.setProperty("TRI_" + DUBBO_PORT_TO_BIND, "50051"); + assertEquals("50051", getValueFromConfig.invoke(null, new ProtocolConfig("tri"), DUBBO_PORT_TO_BIND)); + } finally { + Locale.setDefault(defaultLocale); + } + } + @Test void testToString() { ServiceConfig serviceConfig = new ServiceConfig<>(); diff --git a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java index dbdfc369ab33..60dd385cb509 100644 --- a/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java +++ b/dubbo-config/dubbo-config-spring/src/main/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParser.java @@ -46,6 +46,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; @@ -330,7 +331,7 @@ private static void assignProperties(Object obj, Element ele, ParserContext pars Method[] methods = obj.getClass().getMethods(); for (Method method : methods) { if (MethodUtils.isSetter(method)) { - String beanProperty = method.getName().substring(3, 4).toLowerCase() + String beanProperty = method.getName().substring(3, 4).toLowerCase(Locale.ROOT) + method.getName().substring(4); String property = StringUtils.camelToSplitName(beanProperty, "-"); String value = resolveAttribute(ele, property, parserContext); @@ -395,7 +396,7 @@ private static void getPropertyMap(Class beanClass, Map beanPr && Modifier.isPublic(setter.getModifiers()) && setter.getParameterTypes().length == 1) { Class type = setter.getParameterTypes()[0]; - String beanProperty = name.substring(3, 4).toLowerCase() + name.substring(4); + String beanProperty = name.substring(3, 4).toLowerCase(Locale.ROOT) + name.substring(4); // check the setter/getter whether match Method getter = null; try { diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParserTest.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParserTest.java new file mode 100644 index 000000000000..022fe1521868 --- /dev/null +++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/schema/DubboBeanDefinitionParserTest.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.config.spring.schema; + +import org.apache.dubbo.config.ProtocolConfig; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class DubboBeanDefinitionParserTest { + + // In the Turkish locale "I".toLowerCase() is the dotless "ı", which turned the + // property behind setIothreads() into "ıothreads", so the iothreads attribute was ignored. + @Test + void testGetPropertyMapUnderTurkishLocale() throws Exception { + Method getPropertyMap = + DubboBeanDefinitionParser.class.getDeclaredMethod("getPropertyMap", Class.class, Map.class); + getPropertyMap.setAccessible(true); + Locale defaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.forLanguageTag("tr-TR")); + try { + Map properties = new HashMap<>(); + getPropertyMap.invoke(null, ProtocolConfig.class, properties); + assertEquals( + Integer.class, + properties.get("iothreads"), + properties.keySet().toString()); + } finally { + Locale.setDefault(defaultLocale); + } + } +}