From 930b0d9ad74b7a1b5caeccb0649ce65b07fc9f47 Mon Sep 17 00:00:00 2001 From: Hiroyuki Wada Date: Mon, 4 Jul 2016 17:42:15 +0900 Subject: [PATCH] KEYCLOAK-3278 Add support for any encoding property file in theme --- .../org/keycloak/theme/ClassLoaderTheme.java | 13 +++- .../java/org/keycloak/theme/FolderTheme.java | 13 +++- .../org/keycloak/theme/PropertiesUtil.java | 72 +++++++++++++++++++ .../keycloak/theme/PropertiesUtilTest.java | 55 ++++++++++++++ 4 files changed, 149 insertions(+), 4 deletions(-) mode change 100755 => 100644 services/src/main/java/org/keycloak/theme/ClassLoaderTheme.java mode change 100755 => 100644 services/src/main/java/org/keycloak/theme/FolderTheme.java create mode 100644 services/src/main/java/org/keycloak/theme/PropertiesUtil.java create mode 100644 services/src/test/java/org/keycloak/theme/PropertiesUtilTest.java diff --git a/services/src/main/java/org/keycloak/theme/ClassLoaderTheme.java b/services/src/main/java/org/keycloak/theme/ClassLoaderTheme.java old mode 100755 new mode 100644 index 095832f8e1..24f215a50e --- a/services/src/main/java/org/keycloak/theme/ClassLoaderTheme.java +++ b/services/src/main/java/org/keycloak/theme/ClassLoaderTheme.java @@ -19,7 +19,10 @@ package org.keycloak.theme; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; import java.net.URL; +import java.nio.charset.Charset; import java.util.Locale; import java.util.Properties; @@ -64,7 +67,10 @@ public class ClassLoaderTheme implements Theme { URL p = classLoader.getResource(themeRoot + "theme.properties"); if (p != null) { - properties.load(p.openStream()); + Charset encoding = PropertiesUtil.detectEncoding(p.openStream()); + try (Reader reader = new InputStreamReader(p.openStream(), encoding)) { + properties.load(reader); + } this.parentName = properties.getProperty("parent"); this.importName = properties.getProperty("import"); } else { @@ -127,7 +133,10 @@ public class ClassLoaderTheme implements Theme { URL url = classLoader.getResource(this.messageRoot + baseBundlename + "_" + locale.toString() + ".properties"); if (url != null) { - m.load(url.openStream()); + Charset encoding = PropertiesUtil.detectEncoding(url.openStream()); + try (Reader reader = new InputStreamReader(url.openStream(), encoding)) { + m.load(reader); + } } return m; } diff --git a/services/src/main/java/org/keycloak/theme/FolderTheme.java b/services/src/main/java/org/keycloak/theme/FolderTheme.java old mode 100755 new mode 100644 index d1a28543d6..04621d7528 --- a/services/src/main/java/org/keycloak/theme/FolderTheme.java +++ b/services/src/main/java/org/keycloak/theme/FolderTheme.java @@ -21,7 +21,10 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; import java.net.URL; +import java.nio.charset.Charset; import java.util.Locale; import java.util.Properties; @@ -46,7 +49,10 @@ public class FolderTheme implements Theme { File propertiesFile = new File(themeDir, "theme.properties"); if (propertiesFile .isFile()) { - properties.load(new FileInputStream(propertiesFile)); + Charset encoding = PropertiesUtil.detectEncoding(new FileInputStream(propertiesFile)); + try (Reader reader = new InputStreamReader(new FileInputStream(propertiesFile), encoding)) { + properties.load(reader); + } parentName = properties.getProperty("parent"); importName = properties.getProperty("import"); } @@ -121,7 +127,10 @@ public class FolderTheme implements Theme { File file = new File(themeDir, "messages" + File.separator + baseBundlename + "_" + locale.toString() + ".properties"); if (file.isFile()) { - m.load(new FileInputStream(file)); + Charset encoding = PropertiesUtil.detectEncoding(new FileInputStream(file)); + try (Reader reader = new InputStreamReader(new FileInputStream(file), encoding)) { + m.load(reader); + } } return m; } diff --git a/services/src/main/java/org/keycloak/theme/PropertiesUtil.java b/services/src/main/java/org/keycloak/theme/PropertiesUtil.java new file mode 100644 index 0000000000..18387f7f5c --- /dev/null +++ b/services/src/main/java/org/keycloak/theme/PropertiesUtil.java @@ -0,0 +1,72 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed 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.keycloak.theme; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.jboss.logging.Logger; + +/** + * @author Hiroyuki Wada + */ +public class PropertiesUtil { + + private static final Logger logger = Logger.getLogger(PropertiesUtil.class); + + public static final Pattern DETECT_ENCODING_PATTERN = Pattern.compile("^#\\s*encoding:\\s*([\\w.:-]+)", + Pattern.CASE_INSENSITIVE); + + public static final Charset DEFAULT_ENCODING = Charset.forName("ISO-8859-1"); + + /** + *

+ * Detect file encoding from the first line of the property file. If the first line in the file doesn't contain the + * comment with the encoding, it uses ISO-8859-1 as default encoding for backwards compatibility. + *

+ *

+ * The specified stream is closed before this method returns. + *

+ * + * @param in The input stream + * @return Encoding + * @throws IOException + */ + public static Charset detectEncoding(InputStream in) throws IOException { + try (BufferedReader br = new BufferedReader(new InputStreamReader(in, DEFAULT_ENCODING))) { + String firstLine = br.readLine(); + if (firstLine != null) { + Matcher matcher = DETECT_ENCODING_PATTERN.matcher(firstLine); + if (matcher.find()) { + String encoding = matcher.group(1); + if (Charset.isSupported(encoding)) { + return Charset.forName(encoding); + } else { + logger.warnv("Unsupported encoding: {0}", encoding); + } + } + } + } + return DEFAULT_ENCODING; + } +} diff --git a/services/src/test/java/org/keycloak/theme/PropertiesUtilTest.java b/services/src/test/java/org/keycloak/theme/PropertiesUtilTest.java new file mode 100644 index 0000000000..7e736145d4 --- /dev/null +++ b/services/src/test/java/org/keycloak/theme/PropertiesUtilTest.java @@ -0,0 +1,55 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed 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.keycloak.theme; + +import static org.junit.Assert.assertEquals; + +import java.io.ByteArrayInputStream; +import java.nio.charset.Charset; + +import org.junit.Test; + +/** + * @author Hiroyuki Wada + */ +public class PropertiesUtilTest { + + @Test + public void testDetectEncoding() throws Exception { + Charset encoding = PropertiesUtil.detectEncoding(new ByteArrayInputStream("# encoding: utf-8\nkey=value".getBytes())); + assertEquals(Charset.forName("utf-8"), encoding); + + encoding = PropertiesUtil.detectEncoding(new ByteArrayInputStream("# encoding: Shift_JIS\nkey=value".getBytes())); + assertEquals(Charset.forName("Shift_JIS"), encoding); + } + + @Test + public void testDefaultEncoding() throws Exception { + Charset encoding = PropertiesUtil.detectEncoding(new ByteArrayInputStream("key=value".getBytes())); + assertEquals(Charset.forName("ISO-8859-1"), encoding); + + encoding = PropertiesUtil.detectEncoding(new ByteArrayInputStream("# encoding: unknown\nkey=value".getBytes())); + assertEquals(Charset.forName("ISO-8859-1"), encoding); + + encoding = PropertiesUtil.detectEncoding(new ByteArrayInputStream("\n# encoding: utf-8\nkey=value".getBytes())); + assertEquals(Charset.forName("ISO-8859-1"), encoding); + + encoding = PropertiesUtil.detectEncoding(new ByteArrayInputStream("".getBytes())); + assertEquals(Charset.forName("ISO-8859-1"), encoding); + } +}