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 <[email protected]>
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[