Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 48 additions & 6 deletions src/main/java/org/eclipse/yasson/internal/JsonBinding.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@

package org.eclipse.yasson.internal;

import java.io.FilterInputStream;
import java.io.FilterOutputStream;
import java.io.FilterReader;
import java.io.FilterWriter;
import java.io.InputStream;
import java.io.OutputStream;
Expand Down Expand Up @@ -74,15 +77,15 @@ public <T> T fromJson(String str, Type type) throws JsonbException {

@Override
public <T> T fromJson(Reader reader, Class<T> type) throws JsonbException {
try (JsonParser parser = jsonbContext.getJsonProvider().createParser(reader)) {
try (JsonParser parser = jsonbContext.getJsonProvider().createParser(new CloseSuppressingReader(reader))) {
DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext);
return deserialize(type, parser, unmarshaller);
}
}

@Override
public <T> T fromJson(Reader reader, Type type) throws JsonbException {
try (JsonParser parser = jsonbContext.getJsonProvider().createParser(reader)) {
try (JsonParser parser = jsonbContext.getJsonProvider().createParser(new CloseSuppressingReader(reader))) {
DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext);
return deserialize(type, parser, unmarshaller);
}
Expand All @@ -91,15 +94,15 @@ public <T> T fromJson(Reader reader, Type type) throws JsonbException {
@Override
public <T> T fromJson(InputStream stream, Class<T> clazz) throws JsonbException {
DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext);
try (JsonParser parser = inputStreamParser(stream)) {
try (JsonParser parser = inputStreamParser(new CloseSuppressingInputStream(stream))) {
return deserialize(clazz, parser, unmarshaller);
}
}

@Override
public <T> T fromJson(InputStream stream, Type type) throws JsonbException {
DeserializationContextImpl unmarshaller = new DeserializationContextImpl(jsonbContext);
try (JsonParser parser = inputStreamParser(stream)) {
try (JsonParser parser = inputStreamParser(new CloseSuppressingInputStream(stream))) {
return deserialize(type, parser, unmarshaller);
}
}
Expand Down Expand Up @@ -170,15 +173,15 @@ private JsonGenerator writerGenerator(Writer writer) {
@Override
public void toJson(Object object, OutputStream stream) throws JsonbException {
final SerializationContextImpl marshaller = new SerializationContextImpl(jsonbContext);
try (JsonGenerator generator = streamGenerator(stream)) {
try (JsonGenerator generator = streamGenerator(new CloseSuppressingOutputStream(stream))) {
marshaller.marshall(object, generator);
}
}

@Override
public void toJson(Object object, Type type, OutputStream stream) throws JsonbException {
final SerializationContextImpl marshaller = new SerializationContextImpl(jsonbContext, type);
try (JsonGenerator generator = streamGenerator(stream)) {
try (JsonGenerator generator = streamGenerator(new CloseSuppressingOutputStream(stream))) {
marshaller.marshall(object, generator);
}
}
Expand Down Expand Up @@ -248,4 +251,43 @@ public void close() {

}

private static class CloseSuppressingOutputStream extends FilterOutputStream {

protected CloseSuppressingOutputStream(OutputStream out) {
super(out);
}

@Override
public void close() {
// do not close
}

}

private static class CloseSuppressingInputStream extends FilterInputStream {

protected CloseSuppressingInputStream(InputStream in) {
super(in);
}

@Override
public void close() {
// do not close
}

}

private static class CloseSuppressingReader extends FilterReader {

protected CloseSuppressingReader(Reader in) {
super(in);
}

@Override
public void close() {
// do not close
}

}

}
201 changes: 201 additions & 0 deletions src/test/java/org/eclipse/yasson/Issue586Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
/*
* Copyright (c) 2025 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/

package org.eclipse.yasson;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;

import java.io.ByteArrayInputStream;
import java.io.FilterInputStream;
import java.io.FilterOutputStream;
import java.io.FilterReader;
import java.io.FilterWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.Writer;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;

import org.junit.jupiter.api.Test;

/**
* Verify Jsonb leaves the streams open.
*
* @author Philippe Marschall
*/
public class Issue586Test {

@Test
public void fromJsonInputStreamClass() {
CloseRememberingInputStream stream = new CloseRememberingInputStream(asInputStream("{\"field\":\"first\"}"));
assertNotNull(Jsonbs.defaultJsonb.fromJson(stream, KeyValue.class));
assertFalse(stream.isCloseCalled(), "close() should not be called by ");
}

@Test
public void fromJsonInputStreamType() {
CloseRememberingInputStream stream = new CloseRememberingInputStream(asInputStream("{\"field\":\"first\"}"));
assertNotNull(Jsonbs.defaultJsonb.fromJson(stream, (Type) KeyValue.class));
assertFalse(stream.isCloseCalled(), "close() should not be called by ");
}

@Test
public void fromJsonReaderClass() {
CloseRememberingReader stream = new CloseRememberingReader(asReader("{\"field\":\"first\"}"));
assertNotNull(Jsonbs.defaultJsonb.fromJson(stream, KeyValue.class));
assertFalse(stream.isCloseCalled(), "close() should not be called by ");
}

@Test
public void fromJsonReaderType() {
CloseRememberingReader reader = new CloseRememberingReader(asReader("{\"field\":\"first\"}"));
assertNotNull(Jsonbs.defaultJsonb.fromJson(reader, (Type) KeyValue.class));
assertFalse(reader.isCloseCalled(), "close() should not be called by ");
}
@Test
public void toJsonOutputStreamObject() {
CloseRememberingOutputStream stream = new CloseRememberingOutputStream(OutputStream.nullOutputStream());
Jsonbs.defaultJsonb.toJson(new KeyValue("first"), stream);
assertFalse(stream.isCloseCalled(), "close() should not be called by ");
}

@Test
public void toJsonOutputStreamObjectAndType() {
CloseRememberingOutputStream stream = new CloseRememberingOutputStream(OutputStream.nullOutputStream());
Jsonbs.defaultJsonb.toJson(new KeyValue("first"), KeyValue.class, stream);
assertFalse(stream.isCloseCalled(), "close() should not be called by ");
}

@Test
public void toJsonWriterObject() {
CloseRememberingWriter writer = new CloseRememberingWriter(Writer.nullWriter());
Jsonbs.defaultJsonb.toJson(new KeyValue("first"), writer);
assertFalse(writer.isCloseCalled(), "close() should not be called by ");
}

@Test
public void toJsonWriterObjectAndType() {
CloseRememberingWriter writer= new CloseRememberingWriter(Writer.nullWriter());
Jsonbs.defaultJsonb.toJson(new KeyValue("first"), KeyValue.class, writer);
assertFalse(writer.isCloseCalled(), "close() should not be called by ");
}

private static InputStream asInputStream(String s) {
return new ByteArrayInputStream(s.getBytes(StandardCharsets.US_ASCII));
}

private static Reader asReader(String s) {
return new StringReader(s);
}

public static class KeyValue {

public String field;

public KeyValue() {

}

public KeyValue(String field) {
this.field = field;
}
}

static final class CloseRememberingOutputStream extends FilterOutputStream {

private boolean closeCalled;

CloseRememberingOutputStream(OutputStream out) {
super(out);
this.closeCalled = false;
}

@Override
public void close() throws IOException {
closeCalled = true;
super.close();
}

boolean isCloseCalled() {
return closeCalled;
}

}

static final class CloseRememberingInputStream extends FilterInputStream {

private boolean closeCalled;

CloseRememberingInputStream(InputStream in) {
super(in);
this.closeCalled = false;
}

@Override
public void close() throws IOException {
closeCalled = true;
super.close();
}

boolean isCloseCalled() {
return closeCalled;
}

}

static final class CloseRememberingReader extends FilterReader {

private boolean closeCalled;

CloseRememberingReader(Reader in) {
super(in);
this.closeCalled = false;
}

@Override
public void close() throws IOException {
closeCalled = true;
super.close();
}

boolean isCloseCalled() {
return closeCalled;
}

}

static final class CloseRememberingWriter extends FilterWriter {

private boolean closeCalled;

CloseRememberingWriter(Writer out) {
super(out);
this.closeCalled = false;
}

@Override
public void close() throws IOException {
closeCalled = true;
super.close();
}

boolean isCloseCalled() {
return closeCalled;
}

}

}