This is an automated email from the ASF dual-hosted git repository.

joerghoh pushed a commit to branch SLING-13171-2
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-i18n.git

commit 2a8638c5bde0b0de6e44a5fdcb275abc243d8e09
Author: Joerg Hoh <[email protected]>
AuthorDate: Sat Apr 25 18:00:18 2026 +0200

    SLING-13171 use Apache Johnzon instead of the JCR JsonParser
---
 bnd.bnd                                            |  3 -
 pom.xml                                            | 20 ++++--
 .../apache/sling/i18n/impl/JcrResourceBundle.java  | 80 +++++++---------------
 .../sling/i18n/impl/JcrResourceBundleTest.java     | 41 +++++++++++
 .../org/apache/sling/i18n/it/I18nTestSupport.java  |  8 +++
 5 files changed, 88 insertions(+), 64 deletions(-)

diff --git a/bnd.bnd b/bnd.bnd
index 221feb5..71e3d80 100644
--- a/bnd.bnd
+++ b/bnd.bnd
@@ -10,9 +10,6 @@ Sling-Nodetypes:\
   SLING-INF/nodetypes/jcrlanguage.cnd,\
   SLING-INF/nodetypes/message.cnd
 
--includeresource:\
-  
@jackrabbit-jcr-commons-*.jar!/(org/apache/jackrabbit/util/ISO9075.*|org/apache/jackrabbit/util/XMLChar.*|org/apache/jackrabbit/util/Text.*|org/apache/jackrabbit/commons/json/Json*)
-
 -removeheaders:\
   Include-Resource,\
   Private-Package
diff --git a/pom.xml b/pom.xml
index 883fccf..897cb23 100644
--- a/pom.xml
+++ b/pom.xml
@@ -43,8 +43,8 @@
 
     <properties>
         
<project.build.outputTimestamp>2025-08-20T22:13:35Z</project.build.outputTimestamp>
+        <johnzon.version>1.2.22</johnzon.version>
         <org.ops4j.pax.exam.version>4.14.0</org.ops4j.pax.exam.version>
-        <jackrabbit.version>2.20.0</jackrabbit.version>
         <oak.version>1.22</oak.version>
         <sling.java.version>17</sling.java.version>
         <slf4j.version>2.0.17</slf4j.version>
@@ -111,18 +111,24 @@
             <artifactId>org.osgi.service.metatype.annotations</artifactId>
             <scope>provided</scope>
         </dependency>
-        <dependency>
-            <groupId>org.apache.jackrabbit</groupId>
-            <artifactId>jackrabbit-jcr-commons</artifactId>
-            <version>${jackrabbit.version}</version>
-            <scope>provided</scope>
-        </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.api</artifactId>
             <version>3.0.0</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-json_1.1_spec</artifactId>
+            <version>1.5</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.johnzon</groupId>
+            <artifactId>johnzon-core</artifactId>
+            <version>${johnzon.version}</version>
+            <scope>provided</scope>
+        </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.commons.osgi</artifactId>
diff --git a/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundle.java 
b/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundle.java
index ad9585c..49bab45 100644
--- a/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundle.java
+++ b/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundle.java
@@ -18,8 +18,15 @@
  */
 package org.apache.sling.i18n.impl;
 
+import javax.json.spi.JsonProvider;
+import javax.json.stream.JsonParser;
+import javax.json.stream.JsonParser.Event;
+
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -34,8 +41,6 @@ import java.util.Map;
 import java.util.ResourceBundle;
 import java.util.Set;
 
-import org.apache.jackrabbit.commons.json.JsonHandler;
-import org.apache.jackrabbit.commons.json.JsonParser;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceMetadata;
 import org.apache.sling.api.resource.ResourceResolver;
@@ -245,62 +250,29 @@ public class JcrResourceBundle extends ResourceBundle {
     private void loadJsonDictionary(Resource resource, final Map<String, 
Object> targetDictionary) {
         log.info("Loading json dictionary: {}", resource.getPath());
 
-        // use streaming parser (we don't need the dict in memory twice)
-        JsonParser parser = new JsonParser(new JsonHandler() {
-
-            private String key;
-
-            @Override
-            public void key(String key) throws IOException {
-                this.key = key;
-            }
-
-            @Override
-            public void value(String value) throws IOException {
-                targetDictionary.put(key, value);
-            }
-
-            @Override
-            public void object() throws IOException {}
-
-            @Override
-            public void endObject() throws IOException {}
-
-            @Override
-            public void array() throws IOException {}
-
-            @Override
-            public void endArray() throws IOException {}
-
-            @Override
-            public void value(boolean value) throws IOException {}
-
-            @Override
-            public void value(long value) throws IOException {}
-
-            @Override
-            public void value(double value) throws IOException {}
-        });
-
         final InputStream stream = resource.adaptTo(InputStream.class);
         if (stream != null) {
-            String encoding = "utf-8";
-            final ResourceMetadata metadata = resource.getResourceMetadata();
-            if (metadata.getCharacterEncoding() != null) {
-                encoding = metadata.getCharacterEncoding();
-            }
-
-            try {
-
-                parser.parse(stream, encoding);
+            try (InputStream input = stream) {
+                Charset charset = StandardCharsets.UTF_8;
+                final ResourceMetadata metadata = 
resource.getResourceMetadata();
+                if (metadata != null && metadata.getCharacterEncoding() != 
null) {
+                    charset = Charset.forName(metadata.getCharacterEncoding());
+                }
 
-            } catch (IOException e) {
-                log.warn("Could not parse i18n json dictionary {}: {}", 
resource.getPath(), e.getMessage());
-            } finally {
-                try {
-                    stream.close();
-                } catch (IOException ignore) {
+                try (InputStreamReader reader = new InputStreamReader(input, 
charset);
+                        JsonParser parser = 
JsonProvider.provider().createParser(reader)) {
+                    String key = null;
+                    while (parser.hasNext()) {
+                        final Event event = parser.next();
+                        if (event == Event.KEY_NAME) {
+                            key = parser.getString();
+                        } else if (event == Event.VALUE_STRING && key != null) 
{
+                            targetDictionary.put(key, parser.getString());
+                        }
+                    }
                 }
+            } catch (IOException | RuntimeException e) {
+                log.warn("Could not parse i18n json dictionary {}: {}", 
resource.getPath(), e.getMessage());
             }
         } else {
             log.warn("Not a json file: {}", resource.getPath());
diff --git 
a/src/test/java/org/apache/sling/i18n/impl/JcrResourceBundleTest.java 
b/src/test/java/org/apache/sling/i18n/impl/JcrResourceBundleTest.java
index 38d9ba5..99b5929 100644
--- a/src/test/java/org/apache/sling/i18n/impl/JcrResourceBundleTest.java
+++ b/src/test/java/org/apache/sling/i18n/impl/JcrResourceBundleTest.java
@@ -28,7 +28,9 @@ import java.io.ByteArrayInputStream;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
+import java.lang.reflect.Method;
 import java.net.URL;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.LinkedHashMap;
@@ -36,6 +38,8 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceMetadata;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.testing.mock.jcr.MockJcr;
 import org.apache.sling.testing.mock.jcr.MockQueryResult;
@@ -44,6 +48,7 @@ import org.apache.sling.testing.mock.sling.junit.SlingContext;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.mockito.Mockito;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -447,4 +452,40 @@ public class JcrResourceBundleTest {
         }
         assertEquals(MESSAGES_DE.size(), counter);
     }
+
+    @Test
+    public void test_json_dictionary_with_invalid_encoding_is_ignored() throws 
Exception {
+        Map<String, Object> targetDictionary = new LinkedHashMap<>();
+
+        invokeLoadJsonDictionary(mockJsonResource("{\"key\":\"value\"}", 
"does-not-exist"), targetDictionary);
+
+        assertTrue("dictionary should stay empty for invalid encoding 
metadata", targetDictionary.isEmpty());
+    }
+
+    @Test
+    public void test_json_dictionary_with_invalid_json_is_ignored() throws 
Exception {
+        Map<String, Object> targetDictionary = new LinkedHashMap<>();
+
+        invokeLoadJsonDictionary(mockJsonResource("{\"key\":", 
StandardCharsets.UTF_8.name()), targetDictionary);
+
+        assertTrue("dictionary should stay empty for malformed JSON", 
targetDictionary.isEmpty());
+    }
+
+    private void invokeLoadJsonDictionary(Resource resource, Map<String, 
Object> targetDictionary) throws Exception {
+        JcrResourceBundle bundle = new JcrResourceBundle(new Locale("de"), 
null, resolver, null, new PathFilter());
+        Method method = 
JcrResourceBundle.class.getDeclaredMethod("loadJsonDictionary", Resource.class, 
Map.class);
+        method.setAccessible(true);
+        method.invoke(bundle, resource, targetDictionary);
+    }
+
+    private Resource mockJsonResource(String json, String encoding) {
+        Resource resource = Mockito.mock(Resource.class);
+        ResourceMetadata metadata = Mockito.mock(ResourceMetadata.class);
+        Mockito.when(resource.getPath()).thenReturn("/apps/i18n/de.json");
+        Mockito.when(resource.getResourceMetadata()).thenReturn(metadata);
+        Mockito.when(metadata.getCharacterEncoding()).thenReturn(encoding);
+        Mockito.when(resource.adaptTo(InputStream.class))
+                .thenReturn(new 
ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8)));
+        return resource;
+    }
 }
diff --git a/src/test/java/org/apache/sling/i18n/it/I18nTestSupport.java 
b/src/test/java/org/apache/sling/i18n/it/I18nTestSupport.java
index e4725b3..387e108 100644
--- a/src/test/java/org/apache/sling/i18n/it/I18nTestSupport.java
+++ b/src/test/java/org/apache/sling/i18n/it/I18nTestSupport.java
@@ -76,6 +76,14 @@ public abstract class I18nTestSupport extends TestSupport {
                         .groupId("org.apache.felix")
                         .artifactId("org.apache.felix.http.wrappers")
                         .versionAsInProject(),
+                mavenBundle()
+                        .groupId("org.apache.geronimo.specs")
+                        .artifactId("geronimo-json_1.1_spec")
+                        .version("1.5"),
+                mavenBundle()
+                        .groupId("org.apache.johnzon")
+                        .artifactId("johnzon-core")
+                        .versionAsInProject(),
                 mavenBundle()
                         .groupId("org.apache.sling")
                         .artifactId("org.apache.sling.commons.johnzon")

Reply via email to