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

rmannibucau 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 6571c776 [JOHNZON-369] adding 
org.apache.johnzon.boundedoutputstreamwriter support
6571c776 is described below

commit 6571c7768b4897034a134b8664068b0f8bb2b02e
Author: Romain Manni-Bucau <rmannibu...@gmail.com>
AuthorDate: Wed Apr 27 09:48:53 2022 +0200

    [JOHNZON-369] adding org.apache.johnzon.boundedoutputstreamwriter support
---
 .../johnzon/core/JsonGeneratorFactoryImpl.java     | 20 ++++-
 .../org/apache/johnzon/core/JsonGeneratorImpl.java |  9 --
 .../johnzon/core/io/BoundedOutputStreamWriter.java | 98 ++++++++++++++++++++++
 .../johnzon/core/JsonGeneratorFactoryImplTest.java | 70 ++++++++++++++++
 .../core/io/BoundedOutputStreamWriterTest.java     | 64 ++++++++++++++
 src/site/markdown/index.md                         | 11 +++
 6 files changed, 260 insertions(+), 12 deletions(-)

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 c4748697..af1b905d 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
@@ -18,6 +18,8 @@
  */
 package org.apache.johnzon.core;
 
+import org.apache.johnzon.core.io.BoundedOutputStreamWriter;
+
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.util.Arrays.asList;
 import static java.util.Optional.ofNullable;
@@ -35,21 +37,24 @@ import javax.json.stream.JsonGeneratorFactory;
 
 public class JsonGeneratorFactoryImpl extends AbstractJsonFactory implements 
JsonGeneratorFactory {    
     public static final String GENERATOR_BUFFER_LENGTH = 
"org.apache.johnzon.default-char-buffer-generator";
+    public static final String BOUNDED_OUTPUT_STREAM_WRITER_LEN = 
"org.apache.johnzon.boundedoutputstreamwriter";
     public static final int DEFAULT_GENERATOR_BUFFER_LENGTH =  
Integer.getInteger(GENERATOR_BUFFER_LENGTH, 64 * 1024); //64k
    
     static final Collection<String> SUPPORTED_CONFIG_KEYS = asList(
-        JsonGenerator.PRETTY_PRINTING, GENERATOR_BUFFER_LENGTH, 
BUFFER_STRATEGY, ENCODING
+        JsonGenerator.PRETTY_PRINTING, GENERATOR_BUFFER_LENGTH, 
BUFFER_STRATEGY, ENCODING, BOUNDED_OUTPUT_STREAM_WRITER_LEN
     );
 
     private final Charset defaultEncoding;
 
     //key caching currently disabled
     private final boolean pretty;
+    private final int boundedOutputStreamWriter;
     private final BufferStrategy.BufferProvider<char[]> bufferProvider;
 
     public JsonGeneratorFactoryImpl(final Map<String, ?> config) {
         super(config, SUPPORTED_CONFIG_KEYS, null);
         this.pretty = getBool(JsonGenerator.PRETTY_PRINTING, false);
+        this.boundedOutputStreamWriter = 
getInt(BOUNDED_OUTPUT_STREAM_WRITER_LEN, -1);
         this.defaultEncoding = ofNullable(getString(ENCODING, null))
                 .map(Charset::forName)
                 .orElse(UTF_8);
@@ -68,12 +73,21 @@ public class JsonGeneratorFactoryImpl extends 
AbstractJsonFactory implements Jso
 
     @Override
     public JsonGenerator createGenerator(final OutputStream out) {
-        return new JsonGeneratorImpl(new OutputStreamWriter(out, 
defaultEncoding), bufferProvider, pretty);
+        return new JsonGeneratorImpl(
+                boundedOutputStreamWriter <= 0 ?
+                        new OutputStreamWriter(out, defaultEncoding) :
+                        new BoundedOutputStreamWriter(out, defaultEncoding, 
boundedOutputStreamWriter),
+                bufferProvider, pretty);
     }
 
     @Override
     public JsonGenerator createGenerator(final OutputStream out, final Charset 
charset) {
-        return new JsonGeneratorImpl(out,charset, bufferProvider, pretty);
+        final Charset cs = charset == null ? defaultEncoding : charset;
+        return new JsonGeneratorImpl(
+                boundedOutputStreamWriter <= 0 ?
+                        new OutputStreamWriter(out, cs) :
+                        new BoundedOutputStreamWriter(out, cs, 
boundedOutputStreamWriter),
+                bufferProvider, pretty);
     }
 
     @Override
diff --git 
a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonGeneratorImpl.java 
b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonGeneratorImpl.java
index 0913804f..c011b1f6 100644
--- a/johnzon-core/src/main/java/org/apache/johnzon/core/JsonGeneratorImpl.java
+++ b/johnzon-core/src/main/java/org/apache/johnzon/core/JsonGeneratorImpl.java
@@ -27,13 +27,10 @@ import javax.json.JsonValue;
 import javax.json.stream.JsonGenerationException;
 import javax.json.stream.JsonGenerator;
 import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
 import java.io.Serializable;
 import java.io.Writer;
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.nio.charset.Charset;
 import java.util.Iterator;
 import java.util.Map;
 
@@ -70,18 +67,12 @@ class JsonGeneratorImpl implements JsonGenerator, 
JsonChars, Serializable {
     JsonGeneratorImpl(final Writer writer, final 
BufferStrategy.BufferProvider<char[]> bufferProvider,
                       final boolean prettyPrint) {
         this.writer = writer;
-        //this.cache = cache;
         this.buffer = bufferProvider.newBuffer();
         this.bufferProvider = bufferProvider;
         this.prettyPrint = prettyPrint;
         state.push(GeneratorState.INITIAL);
     }
 
-    JsonGeneratorImpl(final OutputStream out, final Charset encoding, final 
BufferStrategy.BufferProvider<char[]> bufferProvider,
-                      final boolean prettyPrint) {
-        this(new OutputStreamWriter(out, encoding), bufferProvider, 
prettyPrint);
-    }
-
     private void writeEol() {
         if (prettyPrint) {
             justWrite(EOL);
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
new file mode 100644
index 00000000..d0a2100c
--- /dev/null
+++ 
b/johnzon-core/src/main/java/org/apache/johnzon/core/io/BoundedOutputStreamWriter.java
@@ -0,0 +1,98 @@
+/*
+ * 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.johnzon.core.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+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 {@link 
sun.nio.cs.StreamEncoder} with a controlled underlying buffer size.
+ * 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.
+ */
+public class BoundedOutputStreamWriter extends Writer {
+    private final Writer delegate;
+
+    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);
+    }
+
+    @Override
+    public void write(final int c) throws IOException {
+        delegate.write(c);
+    }
+
+    @Override
+    public void write(final char[] chars, final int off, final int len) throws 
IOException {
+        delegate.write(chars, off, len);
+    }
+
+    @Override
+    public void write(final String str, final int off, final int len) throws 
IOException {
+        delegate.write(str, off, len);
+    }
+
+    @Override
+    public void flush() throws IOException {
+        delegate.flush();
+    }
+
+    @Override
+    public void close() throws IOException {
+        delegate.close();
+    }
+
+    @Override
+    public void write(char[] cbuf) throws IOException {
+        delegate.write(cbuf);
+    }
+
+    @Override
+    public void write(final String str) throws IOException {
+        delegate.write(str);
+    }
+
+    @Override
+    public Writer append(final CharSequence csq) throws IOException {
+        return delegate.append(csq);
+    }
+
+    @Override
+    public Writer append(final CharSequence csq, final int start, final int 
end) throws IOException {
+        return delegate.append(csq, start, end);
+    }
+
+    @Override
+    public Writer append(final char c) throws IOException {
+        return delegate.append(c);
+    }
+}
diff --git 
a/johnzon-core/src/test/java/org/apache/johnzon/core/JsonGeneratorFactoryImplTest.java
 
b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonGeneratorFactoryImplTest.java
new file mode 100644
index 00000000..6f02665d
--- /dev/null
+++ 
b/johnzon-core/src/test/java/org/apache/johnzon/core/JsonGeneratorFactoryImplTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.johnzon.core;
+
+import org.junit.Test;
+
+import javax.json.stream.JsonGenerator;
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static java.util.Collections.emptyMap;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+public class JsonGeneratorFactoryImplTest {
+    @Test
+    public void boundedOutputStream() throws UnsupportedEncodingException {
+        final Map<String, Object> boundedConfig = new HashMap<>();
+        
boundedConfig.put(JsonGeneratorFactoryImpl.BOUNDED_OUTPUT_STREAM_WRITER_LEN, 1);
+        boundedConfig.put(JsonGeneratorFactoryImpl.GENERATOR_BUFFER_LENGTH, 1);
+
+        final ByteArrayOutputStream bounded = new ByteArrayOutputStream();
+        final ByteArrayOutputStream defaultOut = new ByteArrayOutputStream();
+
+        try (final JsonGenerator boundedGenerator = new 
JsonGeneratorFactoryImpl(boundedConfig).createGenerator(bounded);
+             final JsonGenerator defaultGenerator = new 
JsonGeneratorFactoryImpl(emptyMap()).createGenerator(defaultOut)) {
+            assertEquals(0, defaultOut.size());
+            assertEquals(0, bounded.size());
+
+            boundedGenerator.writeStartObject();
+            defaultGenerator.writeStartObject();
+            assertEquals(0, defaultOut.size());
+            assertEquals(0, bounded.size());
+
+            boundedGenerator.write("k", "val");
+            defaultGenerator.write("k", "val");
+            assertEquals(0, defaultOut.size());
+            assertEquals(8, bounded.size());
+            // this is the interesting part, there is still some buffering in 
the StreamEncoder due to
+            // encoding logic but it flushes "often enough" for our usage
+            assertEquals("{\"k\":\"va", bounded.toString("UTF-8"));
+
+            boundedGenerator.writeEnd();
+            defaultGenerator.writeEnd();
+            assertEquals(0, defaultOut.size());
+            assertEquals(9, bounded.size());
+        }
+
+        assertArrayEquals(bounded.toByteArray(), defaultOut.toByteArray());
+        assertEquals("{\"k\":\"val\"}", bounded.toString("UTF-8"));
+    }
+}
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
new file mode 100644
index 00000000..2b8f6f6c
--- /dev/null
+++ 
b/johnzon-core/src/test/java/org/apache/johnzon/core/io/BoundedOutputStreamWriterTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.johnzon.core.io;
+
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.junit.Assert.assertEquals;
+
+public class BoundedOutputStreamWriterTest {
+    // sanity check
+    @Test
+    public void write() throws IOException {
+        final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        try (final BoundedOutputStreamWriter writer = new 
BoundedOutputStreamWriter(outputStream, UTF_8, 10)) {
+            writer.write("ok");
+            writer.write('1');
+        }
+        assertEquals("ok1", outputStream.toString("UTF-8"));
+    }
+
+    // enables to check buffer size respects
+    @Test
+    public void sizeLimit() throws IOException {
+        final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        try (final BoundedOutputStreamWriter 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');
+            assertEquals(10, outputStream.size()); // was written
+        }
+        assertEquals("12345678901", outputStream.toString("UTF-8"));
+    }
+
+    // enables to check a small buffer size enables to have a faster 
outputstream feedback
+    @Test
+    public void sizeLimit2() throws IOException {
+        final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        try (final BoundedOutputStreamWriter writer = new 
BoundedOutputStreamWriter(outputStream, UTF_8, 2)) {
+            writer.write("1234567890");
+            writer.write('1');
+        }
+        assertEquals("12345678901", outputStream.toString("UTF-8"));
+    }
+}
diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md
index f97517b5..3f7a4a0b 100644
--- a/src/site/markdown/index.md
+++ b/src/site/markdown/index.md
@@ -52,6 +52,17 @@ You'll surely want to add the API as dependency too:
 </dependency>
 ]]></pre>
 
+#### Johnzon Factory Configurations
+
+##### JsonGeneratorFactory
+
+The generator factory supports the standard properties (pretty one for 
example) but also:
+
+* `org.apache.johnzon.encoding`: encoding to use for the generator when 
converting an OutputStream to a Writer.
+* `org.apache.johnzon.buffer-strategy`: how to get buffers (char buffer), 
default strategy is a queue/pool based one but you can switch it to a 
`THREAD_LOCAL` one. `BY_INSTANCE` (per call/prototype) and `SINGLETON` (single 
instance) are also supported but first one is generally slower and last one 
does not enable overflows.  
+* `org.apache.johnzon.default-char-buffer-generator` (int): buffer size of the 
generator, it enables to work in memory to flush less often (for performances).
+* `org.apache.johnzon.boundedoutputstreamwriter` (int): when converting an 
`OuputStream` to a `Writer` it defines the buffer size (if > 0) +- 2 charaters 
(for the encoding logic). It enables a faster flushing to the actual underlying 
output stream combined with `org.apache.johnzon.default-char-buffer-generator`.
+
 ### JSON-P Strict Compliance (stable)
 
 <pre class="prettyprint linenums"><![CDATA[

Reply via email to