Skip to content

Commit

Permalink
[JOHNZON-404] don't rely on jdk internals for BoundedOutputStreamWrit…
Browse files Browse the repository at this point in the history
…er (#128)

(cherry picked from commit 89492c9)
  • Loading branch information
jungm committed Aug 4, 2024
1 parent 52da80f commit a0e5ffd
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 67 deletions.
7 changes: 3 additions & 4 deletions .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,17 @@ 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: [ '8', '11', '17', '19' ]
jdk: [ '8', '11', '17', '22' ]
dist: [ 'zulu' ]

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up JDK ${{ matrix.jdk }}
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.jdk }}
distribution: ${{ matrix.dist }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,10 @@ public JsonGenerator createGenerator(final Writer writer) {
@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
Expand Down
14 changes: 8 additions & 6 deletions johnzon-core/src/main/java/org/apache/johnzon/core/Snippet.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
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;

Expand Down Expand Up @@ -300,12 +301,13 @@ class SnippetWriter extends PassthroughWriter implements Buffered {
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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {@link 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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

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;
Expand All @@ -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');
}
Expand All @@ -42,7 +43,7 @@ public void write() throws IOException {
@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');
Expand All @@ -55,7 +56,7 @@ public void sizeLimit() throws IOException {
@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');
}
Expand Down

0 comments on commit a0e5ffd

Please sign in to comment.