diff options
4 files changed, 347 insertions, 15 deletions
diff --git a/utils/src/main/java/org/onap/policy/common/utils/coder/Coder.java b/utils/src/main/java/org/onap/policy/common/utils/coder/Coder.java index 41db218b..66a308f7 100644 --- a/utils/src/main/java/org/onap/policy/common/utils/coder/Coder.java +++ b/utils/src/main/java/org/onap/policy/common/utils/coder/Coder.java @@ -20,6 +20,12 @@ package org.onap.policy.common.utils.coder; +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; + /** * JSON encoder and decoder. */ @@ -35,7 +41,34 @@ public interface Coder { String encode(Object object) throws CoderException; /** - * Decodes a json string into an object. + * Encodes an object into json, writing to the given target. + * + * @param target target to which to write the encoded json + * @param object object to be encoded + * @throws CoderException if an error occurs + */ + void encode(Writer target, Object object) throws CoderException; + + /** + * Encodes an object into json, writing to the given target. + * + * @param target target to which to write the encoded json + * @param object object to be encoded + * @throws CoderException if an error occurs + */ + void encode(OutputStream target, Object object) throws CoderException; + + /** + * Encodes an object into json, writing to the given target. + * + * @param target target to which to write the encoded json + * @param object object to be encoded + * @throws CoderException if an error occurs + */ + void encode(File target, Object object) throws CoderException; + + /** + * Decodes json into an object. * * @param json json string to be decoded * @param clazz class of object to be decoded @@ -43,4 +76,34 @@ public interface Coder { * @throws CoderException if an error occurs */ <T> T decode(String json, Class<T> clazz) throws CoderException; + + /** + * Decodes json into an object, reading it from the given source. + * + * @param source source from which to read the json string to be decoded + * @param clazz class of object to be decoded + * @return the object represented by the given json string + * @throws CoderException if an error occurs + */ + <T> T decode(Reader source, Class<T> clazz) throws CoderException; + + /** + * Decodes json into an object, reading it from the given source. + * + * @param source source from which to read the json string to be decoded + * @param clazz class of object to be decoded + * @return the object represented by the given json string + * @throws CoderException if an error occurs + */ + <T> T decode(InputStream source, Class<T> clazz) throws CoderException; + + /** + * Decodes json into an object, reading it from the given source. + * + * @param source source from which to read the json string to be decoded + * @param clazz class of object to be decoded + * @return the object represented by the given json string + * @throws CoderException if an error occurs + */ + <T> T decode(File source, Class<T> clazz) throws CoderException; } diff --git a/utils/src/main/java/org/onap/policy/common/utils/coder/StandardCoder.java b/utils/src/main/java/org/onap/policy/common/utils/coder/StandardCoder.java index 4c7a55c2..389720f9 100644 --- a/utils/src/main/java/org/onap/policy/common/utils/coder/StandardCoder.java +++ b/utils/src/main/java/org/onap/policy/common/utils/coder/StandardCoder.java @@ -21,6 +21,18 @@ package org.onap.policy.common.utils.coder; import com.google.gson.Gson; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.nio.charset.StandardCharsets; /** * JSON encoder and decoder using the "standard" mechanism, which is currently gson. @@ -50,6 +62,42 @@ public class StandardCoder implements Coder { } @Override + public void encode(Writer target, Object object) throws CoderException { + try { + toJson(target, object); + + } catch (RuntimeException | IOException e) { + throw new CoderException(e); + } + } + + @Override + public void encode(OutputStream target, Object object) throws CoderException { + try { + Writer wtr = makeWriter(target); + toJson(wtr, object); + + // flush, but don't close + wtr.flush(); + + } catch (RuntimeException | IOException e) { + throw new CoderException(e); + } + } + + @Override + public void encode(File target, Object object) throws CoderException { + try (Writer wtr = makeWriter(target)) { + toJson(wtr, object); + + // no need to flush or close here + + } catch (RuntimeException | IOException e) { + throw new CoderException(e); + } + } + + @Override public <T> T decode(String json, Class<T> clazz) throws CoderException { try { return fromJson(json, clazz); @@ -59,9 +107,81 @@ public class StandardCoder implements Coder { } } + @Override + public <T> T decode(Reader source, Class<T> clazz) throws CoderException { + try { + return fromJson(source, clazz); + + } catch (RuntimeException e) { + throw new CoderException(e); + } + } + + @Override + public <T> T decode(InputStream source, Class<T> clazz) throws CoderException { + try { + return fromJson(makeReader(source), clazz); + + } catch (RuntimeException e) { + throw new CoderException(e); + } + } + + @Override + public <T> T decode(File source, Class<T> clazz) throws CoderException { + try (Reader input = makeReader(source)) { + return fromJson(input, clazz); + + } catch (RuntimeException | IOException e) { + throw new CoderException(e); + } + } + // the remaining methods are wrappers that can be overridden by junit tests /** + * Makes a writer for the given file. + * + * @param target file of interest + * @return a writer for the file + * @throws FileNotFoundException if the file cannot be created + */ + protected Writer makeWriter(File target) throws FileNotFoundException { + return makeWriter(new FileOutputStream(target)); + } + + /** + * Makes a writer for the given stream. + * + * @param target stream of interest + * @return a writer for the stream + */ + protected Writer makeWriter(OutputStream target) { + return new OutputStreamWriter(target, StandardCharsets.UTF_8); + } + + /** + * Makes a reader for the given file. + * + * @param source file of interest + * @return a reader for the file + * @throws FileNotFoundException if the file does not exist + */ + protected Reader makeReader(File source) throws FileNotFoundException { + return makeReader(new FileInputStream(source)); + } + + /** + * Makes a reader for the given stream. + * + * @param source stream of interest + * @return a reader for the stream + */ + protected Reader makeReader(InputStream source) { + return new InputStreamReader(source, StandardCharsets.UTF_8); + } + + /** * Encodes an object into json, without catching exceptions. * * @param object object to be encoded @@ -72,6 +192,17 @@ public class StandardCoder implements Coder { } /** + * Encodes an object into json, without catching exceptions. + * + * @param target target to which to write the encoded json + * @param object object to be encoded + * @throws IOException if an I/O error occurs + */ + protected void toJson(Writer target, Object object) throws IOException { + GSON.toJson(object, object.getClass(), target); + } + + /** * Decodes a json string into an object, without catching exceptions. * * @param json json string to be decoded @@ -81,4 +212,15 @@ public class StandardCoder implements Coder { protected <T> T fromJson(String json, Class<T> clazz) { return GSON.fromJson(json, clazz); } + + /** + * Decodes a json string into an object, without catching exceptions. + * + * @param source source from which to read the json string to be decoded + * @param clazz class of object to be decoded + * @return the object represented by the given json string + */ + protected <T> T fromJson(Reader source, Class<T> clazz) { + return GSON.fromJson(source, clazz); + } } diff --git a/utils/src/test/java/org/onap/policy/common/utils/coder/StandardCoderTest.java b/utils/src/test/java/org/onap/policy/common/utils/coder/StandardCoderTest.java index 80157d02..25cce748 100644 --- a/utils/src/test/java/org/onap/policy/common/utils/coder/StandardCoderTest.java +++ b/utils/src/test/java/org/onap/policy/common/utils/coder/StandardCoderTest.java @@ -22,17 +22,35 @@ package org.onap.policy.common.utils.coder; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.Arrays; import java.util.List; import org.junit.Before; import org.junit.Test; public class StandardCoderTest { + private static final String EXPECTED_EXCEPTION = "expected exception"; + + private static final JsonParseException jpe = new JsonParseException(EXPECTED_EXCEPTION); + private static final IOException ioe = new IOException(EXPECTED_EXCEPTION); private StandardCoder coder; @@ -42,32 +60,140 @@ public class StandardCoderTest { } @Test - public void testEncode() throws Exception { - List<Integer> arr = Arrays.asList(100, 110); - assertEquals("[100,110]", coder.encode(arr)); + public void testEncodeObject() throws Exception { + List<Integer> arr = Arrays.asList(1100, 1110); + assertEquals("[1100,1110]", coder.encode(arr)); // test exception case - JsonParseException jpe = new JsonParseException("expected exception"); - - coder = spy(coder); + coder = spy(new StandardCoder()); when(coder.toJson(arr)).thenThrow(jpe); - assertThatThrownBy(() -> coder.encode(arr)).isInstanceOf(CoderException.class).hasCause(jpe); } @Test - public void testDecode() throws Exception { - String text = "[200,210]"; - assertEquals(text, coder.decode(text, JsonElement.class).toString()); + public void testEncodeWriterObject() throws Exception { + List<Integer> arr = Arrays.asList(1200, 1210); + StringWriter wtr = new StringWriter(); + coder.encode(wtr, arr); + assertEquals("[1200,1210]", wtr.toString()); - // test exception case - JsonParseException jpe = new JsonParseException("expected exception"); + // test json exception + coder = spy(new StandardCoder()); + doThrow(jpe).when(coder).toJson(wtr, arr); + assertThatThrownBy(() -> coder.encode(wtr, arr)).isInstanceOf(CoderException.class).hasCause(jpe); + } - coder = spy(coder); - when(coder.fromJson(text, JsonElement.class)).thenThrow(jpe); + @Test + public void testEncodeOutputStreamObject() throws Exception { + List<Integer> arr = Arrays.asList(1300, 1310); + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + coder.encode(stream, arr); + assertEquals("[1300,1310]", stream.toString("UTF-8")); + + // test json exception + Writer wtr = new StringWriter(); + coder = spy(new StandardCoder()); + when(coder.makeWriter(stream)).thenReturn(wtr); + doThrow(jpe).when(coder).toJson(wtr, arr); + assertThatThrownBy(() -> coder.encode(stream, arr)).isInstanceOf(CoderException.class).hasCause(jpe); + // test exception when flushed + wtr = spy(new OutputStreamWriter(stream)); + doThrow(ioe).when(wtr).flush(); + coder = spy(new StandardCoder()); + when(coder.makeWriter(stream)).thenReturn(wtr); + assertThatThrownBy(() -> coder.encode(stream, arr)).isInstanceOf(CoderException.class).hasCause(ioe); + } + + @Test + public void testEncodeFileObject() throws Exception { + File file = new File(getClass().getResource(StandardCoder.class.getSimpleName() + ".json").getFile() + "X"); + file.deleteOnExit(); + List<Integer> arr = Arrays.asList(1400, 1410); + coder.encode(file, arr); + assertEquals("[1400,1410]", new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8)); + + // test json exception + StringWriter wtr = new StringWriter(); + coder = spy(new StandardCoder()); + when(coder.makeWriter(file)).thenReturn(wtr); + doThrow(jpe).when(coder).toJson(wtr, arr); + assertThatThrownBy(() -> coder.encode(file, arr)).isInstanceOf(CoderException.class).hasCause(jpe); + + // test exception when closed + coder = spy(new StandardCoder()); + wtr = spy(new StringWriter()); + doThrow(ioe).when(wtr).close(); + coder = spy(new StandardCoder()); + when(coder.makeWriter(file)).thenReturn(wtr); + assertThatThrownBy(() -> coder.encode(file, arr)).isInstanceOf(CoderException.class).hasCause(ioe); + } + + @Test + public void testDecodeStringClass() throws Exception { + String text = "[2200,2210]"; + assertEquals(text, coder.decode(text, JsonElement.class).toString()); + + // test json exception + coder = spy(new StandardCoder()); + when(coder.fromJson(text, JsonElement.class)).thenThrow(jpe); assertThatThrownBy(() -> coder.decode(text, JsonElement.class)).isInstanceOf(CoderException.class) .hasCause(jpe); } + @Test + public void testDecodeReaderClass() throws Exception { + String text = "[2300,2310]"; + assertEquals(text, coder.decode(new StringReader(text), JsonElement.class).toString()); + + // test json exception + coder = spy(new StandardCoder()); + StringReader rdr = new StringReader(text); + when(coder.fromJson(rdr, JsonElement.class)).thenThrow(jpe); + assertThatThrownBy(() -> coder.decode(rdr, JsonElement.class)).isInstanceOf(CoderException.class).hasCause(jpe); + } + + @Test + public void testDecodeInputStreamClass() throws Exception { + String text = "[2400,2410]"; + assertEquals(text, + coder.decode(new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)), JsonElement.class) + .toString()); + + // test json exception + coder = spy(new StandardCoder()); + ByteArrayInputStream stream = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8)); + StringReader rdr = new StringReader(text); + when(coder.makeReader(stream)).thenReturn(rdr); + when(coder.fromJson(rdr, JsonElement.class)).thenThrow(jpe); + assertThatThrownBy(() -> coder.decode(stream, JsonElement.class)).isInstanceOf(CoderException.class) + .hasCause(jpe); + } + + @Test + public void testDecodeFileClass() throws Exception { + File file = new File(getClass().getResource(StandardCoder.class.getSimpleName() + ".json").getFile()); + String text = new String(Files.readAllBytes(file.toPath()), StandardCharsets.UTF_8); + assertEquals(text, coder.decode(file, JsonElement.class).toString()); + + // test FileNotFoundException case + assertThatThrownBy(() -> coder.decode(new File("unknown-file"), JsonElement.class)) + .isInstanceOf(CoderException.class).hasCauseInstanceOf(FileNotFoundException.class); + + // test json exception + Reader rdr = new StringReader(text); + coder = spy(new StandardCoder()); + when(coder.makeReader(file)).thenReturn(rdr); + when(coder.fromJson(rdr, JsonElement.class)).thenThrow(jpe); + assertThatThrownBy(() -> coder.decode(file, JsonElement.class)).isInstanceOf(CoderException.class) + .hasCause(jpe); + + // test IOException case + rdr = spy(new FileReader(file)); + doThrow(ioe).when(rdr).close(); + coder = spy(new StandardCoder()); + when(coder.makeReader(file)).thenReturn(rdr); + assertThatThrownBy(() -> coder.decode(file, JsonElement.class)).isInstanceOf(CoderException.class) + .hasCause(ioe); + } } diff --git a/utils/src/test/resources/org/onap/policy/common/utils/coder/StandardCoder.json b/utils/src/test/resources/org/onap/policy/common/utils/coder/StandardCoder.json new file mode 100644 index 00000000..b50b53b4 --- /dev/null +++ b/utils/src/test/resources/org/onap/policy/common/utils/coder/StandardCoder.json @@ -0,0 +1 @@ +[3000,3010]
\ No newline at end of file |