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

jungm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/johnzon.git


The following commit(s) were added to refs/heads/master by this push:
     new 89492c9b [JOHNZON-404] don't rely on jdk internals for 
BoundedOutputStreamWriter (#128)
89492c9b is described below

commit 89492c9b144995ee3bd3cf243dfe2010bab209cf
Author: Markus Jung <ju...@apache.org>
AuthorDate: Thu Jun 27 14:50:36 2024 +0200

    [JOHNZON-404] don't rely on jdk internals for BoundedOutputStreamWriter 
(#128)
---
 .github/workflows/maven.yml                        |  3 +-
 .../johnzon/core/JsonGeneratorFactoryImpl.java     |  8 +--
 .../main/java/org/apache/johnzon/core/Snippet.java | 14 ++--
 .../johnzon/core/io/BoundedOutputStreamWriter.java | 80 ++++++++--------------
 .../core/io/BoundedOutputStreamWriterTest.java     |  7 +-
 5 files changed, 47 insertions(+), 65 deletions(-)

diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index 19cb2ddd..3dc3fe18 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -5,12 +5,11 @@ on: [push, pull_request]
 jobs:
   build:
     name: Test with Java ${{ matrix.jdk }}
-    #runs-on: ${{ matrix.os }}
     runs-on: ubuntu-latest
     strategy:
       fail-fast: false
       matrix:
-        jdk: [ '11', '17', '21' ]
+        jdk: [ '11', '17', '21', '22' ]
         dist: [ 'zulu' ]
 
     steps:
diff --git 
a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonGeneratorFactoryImpl.java
 
b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonGeneratorFactoryImpl.java
index e5939aa4..641efbf2 100644
--- 
a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonGeneratorFactoryImpl.java
+++ 
b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonGeneratorFactoryImpl.java
@@ -76,10 +76,10 @@ public class JsonGeneratorFactoryImpl extends 
AbstractJsonFactory implements Jso
     @Override
     public JsonGenerator createGenerator(final OutputStream out) {
         return new JsonGeneratorImpl(
-                boundedOutputStreamWriter <= 0 ?
-                        new OutputStreamWriter(out, defaultEncoding) :
-                        new BoundedOutputStreamWriter(out, defaultEncoding, 
boundedOutputStreamWriter),
-                getBufferProvider(out), pretty);
+            boundedOutputStreamWriter <= 0 ?
+                    new OutputStreamWriter(out, defaultEncoding) :
+                    new BoundedOutputStreamWriter(out, defaultEncoding, 
boundedOutputStreamWriter),
+            getBufferProvider(out), pretty);
     }
 
     @Override
diff --git a/johnzon-core/src/main/java/org/apache/johnzon/core/Snippet.java 
b/johnzon-core/src/main/java/org/apache/johnzon/core/Snippet.java
index 02a3b54f..058bbfcf 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/Snippet.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/Snippet.java
@@ -28,6 +28,7 @@ import java.io.ByteArrayOutputStream;
 import java.io.Closeable;
 import java.io.IOException;
 import java.io.Writer;
+import java.nio.charset.Charset;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -300,12 +301,13 @@ public class Snippet {
             public SnippetWriter(final int max) {
                 this.max = max;
                 this.buffer = new ByteArrayOutputStream(max);
-                this.mode = new Writing(max, new BoundedOutputStreamWriter(
-                        buffer,
-                        
JsonGeneratorFactoryImpl.class.isInstance(generatorFactory) ?
-                                
JsonGeneratorFactoryImpl.class.cast(generatorFactory).getDefaultEncoding() :
-                                UTF_8,
-                        max));
+
+                Charset encoding = UTF_8;
+                if 
(JsonGeneratorFactoryImpl.class.isInstance(generatorFactory)) {
+                    encoding = 
JsonGeneratorFactoryImpl.class.cast(generatorFactory).getDefaultEncoding();
+                }
+
+                this.mode = new Writing(max, new 
BoundedOutputStreamWriter(buffer, encoding, max));
             }
 
             public String get() {
diff --git 
a/johnzon-core/src/main/java/org/apache/johnzon/core/io/BoundedOutputStreamWriter.java
 
b/johnzon-core/src/main/java/org/apache/johnzon/core/io/BoundedOutputStreamWriter.java
index a96ca757..897916d5 100644
--- 
a/johnzon-core/src/main/java/org/apache/johnzon/core/io/BoundedOutputStreamWriter.java
+++ 
b/johnzon-core/src/main/java/org/apache/johnzon/core/io/BoundedOutputStreamWriter.java
@@ -18,81 +18,61 @@
  */
 package org.apache.johnzon.core.io;
 
+import java.io.BufferedWriter;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.io.OutputStreamWriter;
 import java.io.Writer;
-import java.nio.channels.Channels;
 import java.nio.charset.Charset;
-import java.nio.charset.CodingErrorAction;
 
 /**
- * {@link java.io.OutputStreamWriter} delegating directly to a 
sun.nio.cs.StreamEncoder with a controlled underlying buffer size.
+ * A {@link BufferedWriter} that wraps an {@link OutputStreamWriter} and 
automatically flushes it when flushing its internal buffer.
  * It enables to wrap an {@link OutputStream} as a {@link Writer} but with a 
faster feedback than a default
- * {@link java.io.OutputStreamWriter} which uses a 8k buffer by default 
(encapsulated).
- * <p>
- * Note: the "flush error" can be of 2 characters (lcb in StreamEncoder) but 
we can't do much better when encoding.
+ * {@link OutputStreamWriter} which uses a 8k buffer by default (encapsulated).
  */
-public class BoundedOutputStreamWriter extends Writer {
-    private final Writer delegate;
+public class BoundedOutputStreamWriter extends BufferedWriter {
+    private final int bufferSize;
 
-    public BoundedOutputStreamWriter(final OutputStream outputStream,
-                                     final Charset charset,
-                                     final int maxSize) {
-        delegate = Channels.newWriter(
-                Channels.newChannel(outputStream),
-                charset.newEncoder()
-                        .onMalformedInput(CodingErrorAction.REPLACE)
-                        .onUnmappableCharacter(CodingErrorAction.REPLACE),
-                maxSize);
-    }
+    private int writtenSinceLastFlush = 0;
 
-    @Override
-    public void write(final int c) throws IOException {
-        delegate.write(c);
-    }
+    public BoundedOutputStreamWriter(OutputStream outputStream, Charset 
charset, int maxSize) {
+        super(new OutputStreamWriter(outputStream, charset), maxSize);
 
-    @Override
-    public void write(final char[] chars, final int off, final int len) throws 
IOException {
-        delegate.write(chars, off, len);
+        this.bufferSize = maxSize;
     }
 
-    @Override
-    public void write(final String str, final int off, final int len) throws 
IOException {
-        delegate.write(str, off, len);
-    }
+    // Only methods that are directly modifying the internal buffer in 
BufferedWriter should be overwritten here,
+    // otherwise we might track the same char being written twice
 
     @Override
-    public void flush() throws IOException {
-        delegate.flush();
-    }
+    public void write(String s, int off, int len) throws IOException {
+        autoFlush();
+        super.write(s, off, len);
 
-    @Override
-    public void close() throws IOException {
-        delegate.close();
+        writtenSinceLastFlush += len;
     }
 
     @Override
-    public void write(char[] cbuf) throws IOException {
-        delegate.write(cbuf);
-    }
+    public void write(char[] cbuf, int off, int len) throws IOException {
+        autoFlush();
+        super.write(cbuf, off, len);
 
-    @Override
-    public void write(final String str) throws IOException {
-        delegate.write(str);
+        writtenSinceLastFlush += len;
     }
 
     @Override
-    public Writer append(final CharSequence csq) throws IOException {
-        return delegate.append(csq);
-    }
+    public void write(int c) throws IOException {
+        autoFlush();
+        super.write(c);
 
-    @Override
-    public Writer append(final CharSequence csq, final int start, final int 
end) throws IOException {
-        return delegate.append(csq, start, end);
+        writtenSinceLastFlush += 1;
     }
 
-    @Override
-    public Writer append(final char c) throws IOException {
-        return delegate.append(c);
+    private void autoFlush() throws IOException {
+        if (writtenSinceLastFlush >= bufferSize) {
+            flush();
+
+            writtenSinceLastFlush = 0;
+        }
     }
 }
diff --git 
a/johnzon-core/src/test/java/org/apache/johnzon/core/io/BoundedOutputStreamWriterTest.java
 
b/johnzon-core/src/test/java/org/apache/johnzon/core/io/BoundedOutputStreamWriterTest.java
index 2b8f6f6c..a10987f8 100644
--- 
a/johnzon-core/src/test/java/org/apache/johnzon/core/io/BoundedOutputStreamWriterTest.java
+++ 
b/johnzon-core/src/test/java/org/apache/johnzon/core/io/BoundedOutputStreamWriterTest.java
@@ -22,6 +22,7 @@ import org.junit.Test;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.Writer;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertEquals;
@@ -31,7 +32,7 @@ public class BoundedOutputStreamWriterTest {
     @Test
     public void write() throws IOException {
         final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        try (final BoundedOutputStreamWriter writer = new 
BoundedOutputStreamWriter(outputStream, UTF_8, 10)) {
+        try (final Writer writer = new BoundedOutputStreamWriter(outputStream, 
UTF_8, 10)) {
             writer.write("ok");
             writer.write('1');
         }
@@ -42,7 +43,7 @@ public class BoundedOutputStreamWriterTest {
     @Test
     public void sizeLimit() throws IOException {
         final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        try (final BoundedOutputStreamWriter writer = new 
BoundedOutputStreamWriter(outputStream, UTF_8, 10)) {
+        try (final Writer writer = new BoundedOutputStreamWriter(outputStream, 
UTF_8, 10)) {
             writer.write("1234567890");
             assertEquals(0, outputStream.size()); // was not yet written since 
it matches buffer size
             writer.write('1');
@@ -55,7 +56,7 @@ public class BoundedOutputStreamWriterTest {
     @Test
     public void sizeLimit2() throws IOException {
         final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        try (final BoundedOutputStreamWriter writer = new 
BoundedOutputStreamWriter(outputStream, UTF_8, 2)) {
+        try (final Writer writer = new BoundedOutputStreamWriter(outputStream, 
UTF_8, 10)) {
             writer.write("1234567890");
             writer.write('1');
         }

Reply via email to